package com.xbongbong.paas.help;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.SizeConstant;
import com.xbongbong.paas.constant.TimeConstant;
import com.xbongbong.paas.domain.entity.PaasStageEntity;
import com.xbongbong.paas.domain.entity.ext.ConditionsEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.elasticsearch.model.PaasFormDataEsModel;
import com.xbongbong.paas.elasticsearch.util.EsUtil;
import com.xbongbong.paas.enums.DefaultTypeEnum;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.EnableEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.enums.MobileIndexModeEnum;
import com.xbongbong.paas.enums.SheetTypeEnum;
import com.xbongbong.paas.enums.StageTypeEnum;
import com.xbongbong.paas.enums.UnitEnum;
import com.xbongbong.paas.field.DefaultAttrPoJo;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.field.ItemPoJo;
import com.xbongbong.paas.field.SubFormPoJo;
import com.xbongbong.paas.pojo.FormTitlePoJo;
import com.xbongbong.paas.pojo.StageFindPojo;
import com.xbongbong.paas.pojo.SummaryDataPoJo;
import com.xbongbong.paas.pojo.dto.FormDataRepeatDTO;
import com.xbongbong.paas.pojo.dto.UserAndDepartmentGetDTO;
import com.xbongbong.paas.pojo.vo.FormDataRepeatVO;
import com.xbongbong.paas.pojo.vo.UserAndDepartmentGetVO;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisConstant;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.redis.RedisTimeConstant;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.CommentUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.DateUtil;
import com.xbongbong.paas.toolbox.util.JsonHelperUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.toolbox.util.TelephoneMaskUtil;
import com.xbongbong.paas.toolbox.wrap.UserVO;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.appdetail.pojo.MobileDetailDialPhonePojo;
import com.xbongbong.pro.constant.CommonConstant;
import com.xbongbong.pro.constant.ElasticConstant;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.constant.SymbolConstant;
import com.xbongbong.pro.defaultData.pojo.DefaultDataPojo;
import com.xbongbong.pro.domain.entity.ChartCategoryEntity;
import com.xbongbong.pro.domain.entity.ChartEntity;
import com.xbongbong.pro.domain.entity.ChartLimitEntity;
import com.xbongbong.pro.domain.entity.FormConfigEntity;
import com.xbongbong.pro.enums.ListBatchResultEnum;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.PackageTypeEnum;
import com.xbongbong.pro.enums.errorcodes.StatisticsServicesErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.TemplateErrorCodeEnum;
import com.xbongbong.pro.enums.log.enums.DetailLogParamEnum;
import com.xbongbong.pro.enums.raiders.enums.RaidersFinishEnum;
import com.xbongbong.pro.formexplain.pojo.dto.HandlerExplainDTO;
import com.xbongbong.pro.listbatch.pojo.TeamBatchPojo;
import com.xbongbong.pro.listbatch.pojo.vo.TeamBatchVO;
import com.xbongbong.pro.log.pojo.DetailLogParamPojo;
import com.xbongbong.pro.packageInfo.pojo.vo.BasePackageInfoVO;
import com.xbongbong.pro.sheet.pojo.SheetAttrPojo;
import com.xbongbong.pro.sheet.pojo.SheetInfoPojo;
import com.xbongbong.pro.sheet.pojo.dto.SheetDeleteHandleRelateDTO;
import com.xbongbong.pro.statistic.constant.ChartConstant;
import com.xbongbong.pro.statistic.enums.ChartCategoryAliasEnum;
import com.xbongbong.pro.statistic.enums.ChartLimitTypeEnum;
import com.xbongbong.pro.statistic.enums.ChartTypeEnum;
import com.xbongbong.pro.statistic.enums.FilterConditionEnum;
import com.xbongbong.pro.statistic.enums.SystemCodeEnum;
import com.xbongbong.pro.statistic.model.ChartLimitModel;
import com.xbongbong.pro.statistic.result.pojo.SalesReportPojo;
import com.xbongbong.pro.user.pojo.ReturnUserAndDepartmentPojo;
import com.xbongbong.pro.weblist.pojo.CustomerCommunicatePojo;
import com.xbongbong.pro.workorder.pojo.TimeConsumingOutPojo;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ListBatchConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.constant.workorder.WorkOrderConsueTimeConstant;
import com.xbongbong.saas.domain.entity.SaleStageEntity;
import com.xbongbong.saas.domain.entity.UserTeamEntity;
import com.xbongbong.saas.domain.entity.WorkOrderEntity;
import com.xbongbong.saas.domain.entity.WorkOrderEvaluateEntity;
import com.xbongbong.saas.enums.CompanyConfigEnum;
import com.xbongbong.saas.enums.CustomerExpressEnum;
import com.xbongbong.saas.enums.CustomerInvoiceEnum;
import com.xbongbong.saas.enums.FormConfigEnum;
import com.xbongbong.saas.enums.InvoiceTitleTypeEnum;
import com.xbongbong.saas.enums.PhoneExtEnum;
import com.xbongbong.saas.enums.SheetAloneEnum;
import com.xbongbong.saas.enums.ShowTypeEnum;
import com.xbongbong.saas.enums.WorkOrderStatusEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.base.CommunicateBaseEnum;
import com.xbongbong.saas.enums.business.ClueEnum;
import com.xbongbong.saas.enums.business.ContactEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.enums.business.CustomerCommunicateEnum;
import com.xbongbong.saas.enums.business.CustomerManagementEnum;
import com.xbongbong.saas.enums.business.InvoiceEnum;
import com.xbongbong.saas.enums.business.PaySheetEnum;
import com.xbongbong.saas.enums.business.PaymentSheetEnum;
import com.xbongbong.saas.enums.business.PurchaseInvoiceEnum;
import com.xbongbong.saas.enums.business.RefundEnum;
import com.xbongbong.saas.enums.business.SalesOpportunityEnum;
import com.xbongbong.saas.enums.business.SupplierCommunicateEnum;
import com.xbongbong.saas.enums.business.WorkOrderEnum;
import com.xbongbong.saas.enums.business.WorkReportPlanEnum;
import com.xbongbong.saas.enums.dictionary.CustomerTypeEnum;
import com.xbongbong.saas.enums.dictionary.DictionaryEnum;
import com.xbongbong.saas.enums.dictionary.ProductionOrderStatusEnum;
import com.xbongbong.saas.model.DataDictionaryModel;
import com.xbongbong.saas.model.FormConfigModel;
import com.xbongbong.saas.model.IndexTypeModel;
import com.xbongbong.saas.model.SaleStageModel;
import com.xbongbong.saas.service.toolbox.help.UserTeamHelp;
import com.xbongbong.saas.toolbox.help.TimeUtil;
import com.xbongbong.saas.util.FundUtil;
import com.xbongbong.sys.domain.entity.CompanyConfigEntity;
import com.xbongbong.sys.domain.entity.DepartmentEntity;
import com.xbongbong.sys.domain.entity.RoleEntity;
import com.xbongbong.sys.domain.entity.TargetLogEntity;
import com.xbongbong.sys.domain.entity.UserEntity;
import com.xbongbong.sys.help.PackageHelp;
import com.xbongbong.sys.model.CompanyConfigModel;
import com.xbongbong.sys.model.DepartmentModel;
import com.xbongbong.sys.model.RoleModel;
import com.xbongbong.sys.model.TargetLogModel;
import com.xbongbong.sys.model.UserModel;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Component;
import org.springframework.validation.FieldError;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.MathContext;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.xbongbong.saas.toolbox.help.FastJsonHelper.getJsonArrOrDefaultFromFormData;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;

/**
 * crm公共方法工具类
 * @author zcp
 * @version v1.0
 * @date 2019/4/17 14:23
 * @since v1.0
 */
@Component
public class CommonHelp {

    private static final Logger LOG = LoggerFactory.getLogger(CommonHelp.class);

    @Resource
    private TargetLogModel targetLogModel;
    @Resource
    private UserModel userModel;
    @Resource
    private DepartmentModel departmentModel;
    @Resource
    private RoleModel roleModel;
    @Resource
    private DataDictionaryModel dataDictionaryModel;
    @Resource
    private SaleStageModel saleStageModel;
    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private CompanyConfigModel companyConfigModel;
    @Resource
    private UserTeamHelp userTeamHelp;
    @Resource
    private PaasFormDataEsModel paasFormDataEsModel;
    @Resource
    private ChartLimitModel chartLimitModel;
    @Resource
    private IndexTypeModel indexTypeModel;
    @Resource
    private PackageHelp packageHelp;
    @Resource
    private FormConfigModel formConfigModel;

    /**
     * 将耗时秒数转换为人性化时间展示
     *
     * @param secs   秒数
     * @param pfefix 前缀
     * @return 转化完成时间String
     * @version 3.20
     * @author zheng.li
     * 添加时间 2018-04-03 上午09:33:29
     * 修改时间：2018-04-03 上午09:33:29
     * @update 2019-5-5 从saas内移入，by zcp
     * @since 3.20
     */
    public String secondsToPersonalTime(Integer secs, String pfefix) {
        if (secs == null) {
            return "";
        }

        StringBuilder sb = new StringBuilder(pfefix);
        //小于一分钟展示秒数，直接返回
        if (secs < TimeConstant.SECONDS_PER_MINUTE) {
            sb.append(secs).append(I18nMessageUtil.getMessage(CommonConstant.SECOND));
            return sb.toString();
        }

        //秒数，转换为小时数
        Double hours = secs / 3600D;
        /*--获取整数位--*/
        Double floor = Math.floor(hours);
        /*--获取小数位--*/
        Double pointValue = hours - floor;

        //分钟数
        Integer minutes = hourToMinute(pointValue);
        Boolean moreThanWeek = floor > 0 || (floor == 0 && minutes == 0);
        if (moreThanWeek) {
            String need = I18nMessageUtil.getMessage(I18nStringConstant.NEED);
            if (floor > TimeConstant.HOURS_PER_WEEK && !Objects.equals(pfefix, need)) {
                sb.append(I18nMessageUtil.getMessage(I18nStringConstant.OVER_SEVEN_DAYS));
            } else {
                sb.append(floor.intValue()).append(I18nMessageUtil.getMessage(CommonConstant.HOUR));
            }
        }

        if (floor <= TimeConstant.HOURS_PER_DAY && !minutes.equals(0)) {
            sb.append(minutes).append(I18nMessageUtil.getMessage(CommonConstant.MINUTE));
        }

        return sb.toString();
    }

    /**
     * Description: 根据工单主表计算超时,用于回显
     *
     * @return com.xbongbong.pro.workorder.pojo.TimeConsumingOutPojo
     * @author 魏荣杰
     * @date 2019/5/14 19:59
     * @updata 2019/5/17 11:27 by zcp 从WorkOrderBasicService把逻辑抽出，保留原方法，内部调用该方法
     * @since v1.0
     */
    public TimeConsumingOutPojo workOrderConsueTime(WorkOrderEntity entity) {
        TimeConsumingOutPojo timeConsumingOutPojo = new TimeConsumingOutPojo();
        String consueTimeStr = "";
        Integer expectTime = entity.getExpectedTime();
        Integer status = entity.getStatus();
        Integer isOut = 0;
        if (Objects.equals(status, WorkOrderStatusEnum.UNACCEPTED.getCode())) {
            consueTimeStr = String.format(I18nMessageUtil.getMessage(WorkOrderConsueTimeConstant.ESTIMATED_TIME_CONSUMING), TimeUtil.getString(expectTime));
        } else if (Objects.equals(status, WorkOrderStatusEnum.ONGOING.getCode())) {
            Integer actualTime = (int) (DateTimeUtil.getInt() - entity.getStartTime());
            if (actualTime > expectTime) {
                consueTimeStr = String.format(I18nMessageUtil.getMessage(WorkOrderConsueTimeConstant.TIME_OUT), TimeUtil.getString(actualTime - expectTime));
                isOut = 1;
            } else {
                consueTimeStr = String.format(I18nMessageUtil.getMessage(WorkOrderConsueTimeConstant.TIME_CONSUMING), TimeUtil.getString(actualTime));
            }
        } else if (Objects.equals(status, WorkOrderStatusEnum.FINISH.getCode())) {
            Integer actualTime = (int) (entity.getEndTime() - entity.getStartTime());
            if (actualTime > expectTime) {
                consueTimeStr = String.format(I18nMessageUtil.getMessage(WorkOrderConsueTimeConstant.TIME_OUT), TimeUtil.getString(actualTime - expectTime));
                isOut = 1;
            } else {
                consueTimeStr = String.format(I18nMessageUtil.getMessage(WorkOrderConsueTimeConstant.TIME_CONSUMING), TimeUtil.getString(actualTime));
            }
        } else if (Objects.equals(status, WorkOrderStatusEnum.CLOSE.getCode())) {
            Integer actualTime = (int) (entity.getCloseTime() - entity.getStartTime());
            if (actualTime > expectTime) {
                consueTimeStr = String.format(I18nMessageUtil.getMessage(WorkOrderConsueTimeConstant.TIME_OUT), TimeUtil.getString(actualTime - expectTime));
                isOut = 1;
            } else {
                consueTimeStr = String.format(I18nMessageUtil.getMessage(WorkOrderConsueTimeConstant.TIME_CONSUMING), TimeUtil.getString(actualTime));
            }
        }
        // 模板没有设置耗时的时候建的数据没有耗时数据，不标记超时红色
        if (Objects.equals(expectTime, 0)) {
            isOut = 0;
        }
        timeConsumingOutPojo.setConsumeTimeStr(consueTimeStr);
        timeConsumingOutPojo.setIsOut(isOut);
        return timeConsumingOutPojo;
    }

    /**
     * Description: 根据工单主表计算超时,用于移动端列表页回显
     *
     * @return com.xbongbong.pro.workorder.pojo.TimeConsumingOutPojo
     * @author long.rao
     * @date 2019-07-08 18:29
     */
    public TimeConsumingOutPojo appWorkOrderConsueTime(WorkOrderEntity entity) {
        TimeConsumingOutPojo timeConsumingOutPojo = new TimeConsumingOutPojo();
        String consueTimeStr = "";
        JSONObject data = entity.getData();
        Integer expectTime = data.getInteger("expectedTime");
        expectTime = expectTime == null ? 0 : expectTime;
        Integer status = data.getInteger(WorkOrderEnum.STATUS.getSaasAttr());
        Integer endTime = data.getInteger(WorkOrderEnum.COMPLETE_TIME.getSaasAttr());
        Integer closeTime = data.getInteger(WorkOrderEnum.CLOSE_TIME.getSaasAttr());
        Integer startTime = data.getInteger("startTime");
        Integer isOut = 0;
        if (Objects.equals(status, WorkOrderStatusEnum.UNACCEPTED.getCode())) {
            consueTimeStr = String.format(I18nMessageUtil.getMessage(WorkOrderConsueTimeConstant.ESTIMATED_TIME_CONSUMING), TimeUtil.getString(expectTime));
        } else if (Objects.equals(status, WorkOrderStatusEnum.ONGOING.getCode())) {
            Integer actualTime = (int) (DateTimeUtil.getInt() - startTime);
            if (actualTime > expectTime) {
                consueTimeStr = String.format(I18nMessageUtil.getMessage(WorkOrderConsueTimeConstant.TIME_OUT), TimeUtil.getString(actualTime - expectTime));
                isOut = 1;
            } else {
                consueTimeStr = String.format(I18nMessageUtil.getMessage(WorkOrderConsueTimeConstant.TIME_CONSUMING), TimeUtil.getString(actualTime));
            }
        } else if (Objects.equals(status, WorkOrderStatusEnum.FINISH.getCode())) {
            Integer actualTime = (int) (endTime - startTime);
            if (actualTime > expectTime) {
                consueTimeStr = String.format(I18nMessageUtil.getMessage(WorkOrderConsueTimeConstant.TIME_OUT), TimeUtil.getString(actualTime - expectTime));
                isOut = 1;
            } else {
                consueTimeStr = String.format(I18nMessageUtil.getMessage(WorkOrderConsueTimeConstant.TIME_CONSUMING), TimeUtil.getString(actualTime));
            }
        } else if (Objects.equals(status, WorkOrderStatusEnum.CLOSE.getCode())) {
            Integer actualTime = (int) (closeTime - startTime);
            if (actualTime > expectTime) {
                consueTimeStr = String.format(I18nMessageUtil.getMessage(WorkOrderConsueTimeConstant.TIME_OUT), TimeUtil.getString(actualTime - expectTime));
                isOut = 1;
            } else {
                consueTimeStr = String.format(I18nMessageUtil.getMessage(WorkOrderConsueTimeConstant.TIME_CONSUMING), TimeUtil.getString(actualTime));
            }
        }
        timeConsumingOutPojo.setConsumeTimeStr(consueTimeStr);
        timeConsumingOutPojo.setIsOut(isOut);
        return timeConsumingOutPojo;
    }

    //-------------------------员工、部门公共方法-------------------------

    /**
     * 通过员工ids获取员工实体列表(包含离职员工)
     *
     * @param corpid   公司id
     * @param userIdIn 员工ids
     * @return java.util.List<com.xbongbong.sys.domain.entity.UserEntity>
     * @author zcp
     * @date 2019/4/30 21:28
     * @version v1.0
     * @since v1.0
     */
    public List<UserEntity> getUserListByIds(String corpid, Collection<String> userIdIn) {
        if (userIdIn == null || userIdIn.size() == 0) {
            return new ArrayList<>();
        }
        //查询时添加离职员工
        Set<Integer> delIn = new HashSet<>();
        delIn.add(DelEnum.NORMAL.getDel());
        delIn.add(DelEnum.DELETE.getDel());
        //查询图表分类的参数
        Map<String, Object> param = new HashMap<>(3);
        param.put("corpid", corpid);
        param.put("delIn", delIn);
        param.put("userIdIn", userIdIn);
        List<UserEntity> userEntities = userModel.findEntitys(param);
        //离职员工名称特殊处理
        for (UserEntity user : userEntities) {
            if (Objects.equals(user.getDel(), DelEnum.DELETE.getDel())) {
                String name = user.getName();
                user.setName(name + "(" + I18nMessageUtil.getMessage(CommonConstant.LEAVE) + ")");
            }
        }
        return userEntities;
    }

    /**
     * 通过id列表获取员工list并构建id和entity的对应关系
     *
     * @param corpid   公司id
     * @param userIdIn 员工id数组
     * @return java.util.Map<java.lang.Long , com.xbongbong.saas.domain.entity.ProductCategoryEntity>
     * @author zcp
     * @date 2019/4/8 21:25
     * @version v1.0
     * @since v1.0
     */
    public Map<String, UserEntity> getUserMapByIds(String corpid, Collection<String> userIdIn) {
        List<UserEntity> list = getUserListByIds(corpid, userIdIn);
        //员工id--员工信息
        Map<String, UserEntity> map = new HashMap<>(list.size());
        for (UserEntity entity : list) {
            map.put(entity.getUserId(), entity);
        }
        return map;
    }

    /**
     * 通过id列表获取员工列表，并构建员工id和员工名称的对应关系
     *
     * @param corpid   公司id
     * @param userIdIn 员工id数组
     * @return java.util.Map<java.lang.String , java.lang.String>
     * @author zcp
     * @date 2019/4/30 21:30
     * @version v1.0
     * @since v1.0
     */
    public Map<String, String> getUserNameMapByIds(String corpid, Collection<String> userIdIn) {
        List<UserEntity> list = getUserListByIds(corpid, userIdIn);
        //员工id--员工名称
        Map<String, String> map = new HashMap<>(list.size());
        for (UserEntity entity : list) {
            map.put(entity.getUserId(), entity.getName());
        }
        return map;
    }

    /**
     * 按需封装员工、部门、角色容器
     * @param corpid 公司id
     * @param detailLogParamEnumList 需要封装的容器枚举（根据需要塞入要获取的容器对应的枚举）
     * @return com.xbongbong.pro.log.pojo.DetailLogParamPojo
     * @throws XbbException
     * @author zcp
     * @date 2019/8/14 14:15
     * @update 2019/8/14 14:15
     * @since v1.0
     * @version v1.0
     */
    public DetailLogParamPojo getDetailLogParamPojo4UserDepRole(String corpid, List<DetailLogParamEnum> detailLogParamEnumList) throws XbbException {
        DetailLogParamPojo detailLogParamPojo = new DetailLogParamPojo();
        if (detailLogParamEnumList == null || detailLogParamEnumList.size() == 0) {
            return detailLogParamPojo;
        }
        UserAndDepartmentGetDTO userAndDepartmentGetDTO = new UserAndDepartmentGetDTO();
        userAndDepartmentGetDTO.setCorpid(corpid);
        for (DetailLogParamEnum cache : detailLogParamEnumList) {
            switch (cache) {
                case USER:
                    Map<String, UserEntity> userMap = getUserMap(userAndDepartmentGetDTO,null);
                    detailLogParamPojo.setUserMap(userMap);
                    break;
                case DEPARTMENT:
                    Map<String, DepartmentEntity> depMap = getDepMap(userAndDepartmentGetDTO);
                    detailLogParamPojo.setDepartmentMap(depMap);
                    break;
                case ROLE:
                    Map<String, RoleEntity> roleMap = getRoleMap(corpid);
                    detailLogParamPojo.setRoleMap(roleMap);
                    break;
                default:
                    break;
            }
        }
        return detailLogParamPojo;
    }

    /**
     * 获取用户和部门的容器
     *
     * @param userAndDepartmentGetDTO
     * @return
     * @throws XbbException
     * @update 2019-7-2 by zcp 从main内的UserHelp类移入
     */
    public UserAndDepartmentGetVO getUserDepartmentMap(UserAndDepartmentGetDTO userAndDepartmentGetDTO,Integer LeaveMarks) throws XbbException {
        UserAndDepartmentGetVO userAndDepartmentGetVO = new UserAndDepartmentGetVO();
        try {
            Map<String, UserEntity> userMap = getUserMap(userAndDepartmentGetDTO,LeaveMarks);
            Map<String, DepartmentEntity> departmentMap = getDepMap(userAndDepartmentGetDTO);
            userAndDepartmentGetVO.setUserMap(userMap);
            userAndDepartmentGetVO.setDepartmentMap(departmentMap);
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("userHelp.getUserDepartmentMap获取用户和部门的容器出错", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return userAndDepartmentGetVO;
    }

    /**
     * 获取部门容器
     * @param userAndDepartmentGetDTO
     * @return java.util.Map<java.lang.String,com.xbongbong.sys.domain.entity.DepartmentEntity>
     * @throws XbbException
     * @author zcp
     * @date 2019/8/14 9:57
     * @update 2019/8/14 9:57
     * @since v1.0
     * @version v1.0
     */
    public Map<String, DepartmentEntity> getDepMap(UserAndDepartmentGetDTO userAndDepartmentGetDTO) throws XbbException {
        Map<String, DepartmentEntity> departmentMap = userAndDepartmentGetDTO.getDepartmentMap();
        if (!departmentMap.isEmpty()) {
            return departmentMap;
        }
        Map<String, Object> param = BeanUtil.convertBean2Map(userAndDepartmentGetDTO, true);
        List<DepartmentEntity> departmentList = departmentModel.findEntitys(param);
        if (departmentList == null) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.DEPT_NOT_EXIST);
        }
        departmentList.forEach((item) -> {
            departmentMap.put(item.getId().toString(), item);
        });
        return departmentMap;
    }

    /**
     * 获取所有用户的容器
     *
     * @param userAndDepartmentGetDTO
     * @return
     * @throws XbbException
     * @update 2019-7-2 by zcp 从main内的UserHelp类移入
     */
    public Map<String, UserEntity> getUserMap(UserAndDepartmentGetDTO userAndDepartmentGetDTO,Integer leaveMarks) throws XbbException {
        Map<String, UserEntity> userMap = userAndDepartmentGetDTO.getUserMap();
        if (!userMap.isEmpty()) {
            return userMap;
        }
        Map<String, Object> param = BeanUtil.convertBean2Map(userAndDepartmentGetDTO, true);
        CommentUtil.addToMap(param,ParameterConstant.LEAVE_MARKS,leaveMarks);
        List<UserEntity> userList = userModel.findEntitys(param);
        if (userList == null || userList.size() == 0) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.USER_NOT_EXIST);
        }
        userList.forEach((item) -> {
            userMap.put(item.getUserId(), item);
        });
        return userMap;
    }

    /**
     * 获取所有用户的容器
     * @param userAndDepartmentGetDTO
     * @return
     * @throws XbbException
     */
    public Map<String, DepartmentEntity> getDepartmentMap(UserAndDepartmentGetDTO userAndDepartmentGetDTO) throws XbbException {
        Map<String, DepartmentEntity> departmentMap = userAndDepartmentGetDTO.getDepartmentMap();
        if (!departmentMap.isEmpty()) {
            return departmentMap;
        }
        Map<String, Object> param = BeanUtil.convertBean2Map(userAndDepartmentGetDTO, true);
        List<DepartmentEntity> departmentList = departmentModel.findEntitys(param);
        if (departmentList == null) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.DEPT_NOT_EXIST);
        }
        departmentList.forEach((item) -> {
            departmentMap.put(item.getId().toString(), item);
        });
        return departmentMap;
    }

    /**
     * 获取所有角色的容器
     * @param corpid
     * @return java.util.Map<java.lang.String,com.xbongbong.sys.domain.entity.RoleEntity>
     * @throws XbbException
     * @author zcp
     * @date 2019/8/14 11:32
     * @update 2019/8/14 11:32
     * @since v1.0
     * @version v1.0
     */
    public Map<String, RoleEntity> getRoleMap(String corpid) throws XbbException {
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        List<RoleEntity> roleList = roleModel.findEntitys(param);
        if (roleList == null) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.ROLE_NOT_EXIST);
        }
        Map<String, RoleEntity> roleMap = new HashMap<>(roleList.size());
        for (RoleEntity entity : roleList) {
            roleMap.put(entity.getId().toString(), entity);
        }
        return roleMap;
    }

    //-------------------------手机号格式化方法-------------------------

    /**
     * 格式化子表单内手机号，返回字符串形式的联系方式
     *
     * @param value 手机号数据对象
     * @return java.lang.String
     * @author zcp
     * @date 2019/7/3 22:18
     * @version v1.0
     * @since v1.0
     */
    public String formatPhone(Object value) {
        if (value == null) {
            return "";
        }
        JSONArray phoneJsonArray = JsonHelperUtil.parseArray(value.toString());
        return formatPhone(phoneJsonArray);
    }

    /**
     * 格式化子表单内手机号，返回字符串形式的联系方式
     *
     * @param phoneJsonArray 联系方式对象
     * @return java.lang.String
     * @author zcp
     * @date 2019/5/3 19:12
     * @version v1.0
     * @since v1.0
     */
    public String formatPhone(JSONArray phoneJsonArray) {
        if (phoneJsonArray == null) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        String attrName = StringConstant.PHONE_TYPE_ATTR;
        String attrTel = StringConstant.PHONE_TEL_ATTR;
        phoneJsonArray.forEach(value -> {
            JSONObject item = (JSONObject) value;
            String name = item.getString(attrName);
            String tel = item.getString(attrTel);
            if (StringUtil.isEmpty(tel)) {
                return;
            }
            if (sb.length() > 0) {
                sb.append(SymbolConstant.SEMICOLON_CHINESE);
            }
            //code转为名字
            name = PhoneExtEnum.getNameByCode(name);
            sb.append(name).append(SymbolConstant.COLON_ENGLISH).append(tel);
        });
        return sb.toString();
    }

    //-------------------------团队方法-------------------------

    /**
     * 得到业务dataId和负责人的对应关系map
     *
     * @param userTeamList 负责人list
     * @return java.util.Map<java.lang.Long , java.lang.String>
     * @author zcp
     * @date 2019/4/10 11:27 从saas移入
     * @version v1.0
     * @since v1.0
     */
    public <T> Map<Long, String> getUserTeamNameMap(List<T> userTeamList) {
        Set<String> userIdIn = new HashSet<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        UserTeamEntity userTeamEntity = null;
        Map<String, String> userMap = new HashMap<>(userTeamList.size());
        for (T t : userTeamList) {
            userTeamEntity = (UserTeamEntity) t;
            userIdIn.add(userTeamEntity.getUserId());
        }
        if (CollectionsUtil.isEmpty(userTeamList)) {
            return new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        }
        //将含有离职的员工后面添加"(离职)"标记
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        userTeamEntity = (UserTeamEntity)userTeamList.get(0);
        String corpid = userTeamEntity.getCorpid();
        param.put("corpid", corpid);
        param.put("userIdIn", userIdIn);
        param.put(ParameterConstant.LEAVE_MARKS, BasicConstant.ONE);
        List<UserEntity> userList = userModel.findEntitys(param);
        for (UserEntity entity : userList) {
            userMap.put(entity.getUserId(), entity.getName());
        }
        //dataId--负责人名称（dataId即关联业务的id）
        Map<Long, String> userNameMap = new HashMap<>(userTeamList.size());
        for (T t : userTeamList) {
            userTeamEntity = (UserTeamEntity) t;
            Long dataId = userTeamEntity.getDataId();
            String userName = userNameMap.get(dataId);
            String name = userMap.get(userTeamEntity.getUserId());
            if (!StringUtil.isEmpty(userName)) {
                userName += "," + name;
            } else {
                userName = name;
            }
            userNameMap.put(dataId, userName);
        }
        return userNameMap;
    }

    //-------------------------销售简报方法-------------------------

    /**
     * 保存用户的销售简报推送
     *
     * @param salesReportPojo 塞入结果用pojo，主要用到如下字段：uidTargetMap、userEntity、reportTargetMap、dayStartTime、dayEndTime
     * @version 3.11
     * @author cp.zhang
     * @date 2017-8-10 下午7:57:25
     * @update 2017-8-25 上午10:47:45 by zcp 把排重的查询操作放到外面传入，放外面一个公司查一次
     * @update 2019-4-17 从saas移入，并处理成pro调用方式 by zcp
     * @since 3.11
     */
    public void saveSalesReport(SalesReportPojo salesReportPojo) throws XbbException {
        /**
         key 为指标alias，@see ReportTargetEnum枚举的alias
         value 为统计出的该指标的总额，格式为{“day”:0,”month”:0}
         */
        Map<String, JSONObject> reportTargetMap = salesReportPojo.getReportTargetMap();
        if (reportTargetMap == null) {
            //为空，不用塞入指标log表
            return;
        }
        UserEntity userEntity = salesReportPojo.getLogUserEntity();
        if (userEntity == null) {
            return;
        }
        //userId--对应的log实体
        Map<String, TargetLogEntity> uidTargetMap = salesReportPojo.getUidTargetMap();
        String corpid = userEntity.getCorpid();
        String userId = userEntity.getUserId();
        Integer now = DateUtil.getInt();

        TargetLogEntity target = new TargetLogEntity();
        target.setCorpid(corpid);
        target.setUserId(userId);
        target.setStartTime(salesReportPojo.getDayStartTime());
        target.setEndTime(salesReportPojo.getDayEndTime());
        //置入指标
        target.setReportTarget(reportTargetMap);
        target.setAddTime(now);

        TargetLogEntity userLog = (uidTargetMap == null) ? null : uidTargetMap.get(userId);
        if (userLog != null) {
            target.setId(userLog.getId());
            target.setAddTime(userLog.getAddTime());
        }
        target.setUpdateTime(now);
        target.setDel(0);

        targetLogModel.save(target);
    }

    //-------------------------数据字典-------------------------

    /**
     * 把数据字典列表，转为map（无需map，只为了选值）
     *
     * @param itemPojoList 字典list
     * @return java.util.Map<java.lang.String , java.lang.String>
     * @author zcp
     * @date 2019/7/3 19:42
     * @version v1.0
     * @since v1.0
     */
    public Map<String, String> listToMap(List<ItemPoJo> itemPojoList) {
        if (itemPojoList == null) {
            return new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        }
        Map<String, String> dictionaryMap = new HashMap<>(itemPojoList.size());
        for (ItemPoJo itemPojo : itemPojoList) {
            dictionaryMap.put(itemPojo.getValue().toString(), itemPojo.getText());
        }
        return dictionaryMap;
    }

    public Map<Long, Map<String, String>> getStageMapByFormIdIn(String corpid, List<Long> formIdIn) {
        Map<String,Object> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        map.put(ParameterConstant.CORPID, corpid);
        map.put("formIdIn", formIdIn);
        map.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
        List<SaleStageEntity> list = saleStageModel.findEntitys(map);
        Map<Long, List<SaleStageEntity>> formIdStages = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        list.forEach(saleStageEntity -> {
            List<SaleStageEntity> stageEntityList = new ArrayList<>();
            if (formIdStages.containsKey(saleStageEntity.getFormId())) {
                stageEntityList.addAll(formIdStages.get(saleStageEntity.getFormId()));
            }
            stageEntityList.add(saleStageEntity);
            formIdStages.put(saleStageEntity.getFormId(), stageEntityList);
        });
        Map<Long, Map<String, String>> formStageMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        formIdStages.forEach((key, value) -> {
            Map<String, String> stageMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            value.forEach(stage ->
                    stageMap.put(stage.getCode().toString(), stage.getName())
            );
            formStageMap.put(key, stageMap);
        });
        return formStageMap;
    }

    /**
     * 获取有效客户状态：所有启用的客户状态内，排除“无效客户”的客户状态即为全部有效客户状态
     *
     * @param corpid 公司id
     * @return java.util.Set<java.lang.String>
     * @throws XbbException
     * @author zcp
     * @date 2019/5/1 9:48
     * @version v1.0
     * @since v1.0
     */
    public Set<String> getCustomerTypeIn4Valid(String corpid) throws XbbException {
        //获取启用的客户状态，公司级别数据字典，不用限制formId
        Map<String, String> customerStatusMap = dataDictionaryModel.getDictionaryMap(DictionaryEnum.CUSTOMER_STATUS, corpid, null);
        Set<String> typeIn = new HashSet<>();
        //加上没有设置客户状态的客户
        typeIn.add("");
        typeIn.add("0");
        //有效客户
        for (Map.Entry<String, String> entry : customerStatusMap.entrySet()) {
            //排除“无效客户”的客户状态
            if (!(CustomerTypeEnum.INVALID_CUSTOMER.getCode()).equals(entry.getKey())) {
                typeIn.add(entry.getKey());
            }
        }
        return typeIn;
    }

    /**
     * 获取无效客户状态：所有启用的客户状态内，名称为“无效客户”的客户状态
     *
     * @param corpid 公司id
     * @return java.lang.String
     * @throws XbbException
     * @author zcp
     * @date 2019/5/1 20:02
     * @version v1.0
     * @since v1.0
     */
    public String getCustomerType4Lose(String corpid) throws XbbException {
        //获取启用的客户状态，公司级别数据字典，不用限制formId
        Map<String, String> customerStatusMap = dataDictionaryModel.getDictionaryMap(DictionaryEnum.CUSTOMER_STATUS, corpid, null);
        String typeLose = "-1";
        //无效客户
        for (Map.Entry<String, String> entry : customerStatusMap.entrySet()) {
            //排除“无效客户”的客户状态
            if ((CustomerTypeEnum.INVALID_CUSTOMER.getName()).equals(entry.getValue())) {
                typeLose = entry.getKey();
            }
        }
        return typeLose;
    }

    /**
     * 格式化某个数据字典穿透后在标题处的显示值，拼接成的格式为：(xxx：数据字典值)，如，(客户行业：科技)
     *
     * @param dictionaryCode 穿透的数据字典code
     * @param prefix         前缀，一般为某个数据字典名，如，客户行业、跟进方式等
     * @param dictionaryList 数据字典list
     * @return java.lang.String
     * @author zcp
     * @date 2019/5/2 19:41
     * @version v1.0
     * @since v1.0
     */
    public String formatThroughDictionary(String dictionaryCode, String prefix, List<ItemPoJo> dictionaryList) {
        String codeTitle = "";
        for (ItemPoJo itemPojo : dictionaryList) {
            String code = itemPojo.getValue().toString();
            if (Objects.equals(dictionaryCode, code)) {
                StringBuilder str = new StringBuilder();
                //拼接成的格式为：(xxx：数据字典值)，如，(客户行业：科技)
                str.append("(").append(prefix).append("：").append(itemPojo.getText()).append(")");
                codeTitle = str.toString();
                break;
            }
        }
        return codeTitle;
    }

    //-------------------------生产单-------------------------

    /**
     * 判断生产单是否未超时
     *
     * @param planTime     计划完成时间
     * @param completeTime 完成时间
     * @param closeTime    关闭时间
     * @param statusEnum   生产单状态枚举
     * @return boolean true未超时，false超时
     * @author zcp
     * @date 2019/5/4 19:29
     * @version v1.0
     * @since v1.0
     */
    public boolean ifNotOvertime(Long planTime, Long completeTime, Long closeTime, ProductionOrderStatusEnum statusEnum) {
        //状态为null时为未超时
        if (ProductionOrderStatusEnum.UNKNOW == statusEnum || null == statusEnum) {
            return true;
        }
        //计划时间为null时为超时
        if (null == planTime) {
            return true;
        }
        switch (statusEnum) {
            case UNFINISHED:
                //未完成状态下，计划时间>=当前时间为未超时
                return planTime >= DateUtil.getInt();
            case FINISHED:
                //完成状态下，完成时间为空或完成时间<=计划时间为未超时
                return (null == completeTime || (completeTime <= planTime));
            case CLOSED:
                //关闭状态下，关闭时间为空或关闭时间<=计划时间为未超时
                return (null == closeTime || (closeTime <= planTime));
            default:
                return false;
        }
    }

    //-------------------------生产单-------------------------

    /**
     * 构建工单id和评价星级的对应关系
     *
     * @param workOrderEvaluateEntityList 评价list
     * @return java.util.Map<java.lang.Long , java.lang.Integer>
     * @author zcp
     * @date 2019/5/5 16:14
     * @version v1.0
     * @since v1.0
     */
    public Map<Long, Integer> getOrderStarMap(List<WorkOrderEvaluateEntity> workOrderEvaluateEntityList) {
        //工单id--评价星级
        Map<Long, Integer> orderStarMap = new HashMap<>(workOrderEvaluateEntityList.size());
        for (WorkOrderEvaluateEntity entity : workOrderEvaluateEntityList) {
            orderStarMap.put(entity.getWorkOrderId(), entity.getRateStar());
        }
        return orderStarMap;
    }

    /**
     * 得到关联业务的名称（or编号or...，即idValueMap内的value拼接）拼接名
     *
     * @param idValueMap id和编号or名称等的对应关系map
     * @param idArr      关联的id数组，格式为[1, 2]
     * @return java.lang.String
     * @author zcp
     * @date 2019/5/9 20:47
     * @version v1.0
     * @since v1.0
     */
    public String getRelationNo(Map<Long, String> idValueMap, JSONArray idArr) {
        if (idArr == null) {
            return "";
        }
        List<Long> idIn = JsonHelperUtil.parseArray(idArr.toString(), Long.class);
        StringBuilder str = new StringBuilder();
        for (Long id : idIn) {
            String value = idValueMap.get(id);
            if (StringUtil.isEmpty(value)) {
                continue;
            }
            if (str.length() != 0) {
                str.append(",");
            }
            str.append(value);
        }
        return str.toString();
    }

    /**
     * 筛选出当前id集合所对应的字段关系
     *
     * @param idAttrsMap id对应的多个字段关系
     * @param idArr 数据id集合
     * @return java.util.List<java.util.Map<java.lang.String,java.lang.String>>
     * @author 徐俊杰
     * @date 2020/2/19 14:40
     * @since v1.0
     */
    public List<Map<String, String>> getRelatedMapList(Map<Long, Map<String, String>> idAttrsMap, JSONArray idArr) {
        List<Map<String, String>> relatedMapList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (idArr == null) {
            return relatedMapList;
        }
        List<Long> idIn = JsonHelperUtil.parseArray(idArr.toString(), Long.class);
        for (Long id : idIn) {
            Map<String, String> map = idAttrsMap.get(id);
            if (map != null) {
                relatedMapList.add(map);
            }
        }
        return relatedMapList;
    }

    /**
     * 得到关联业务的名称（or编号or...，即idValueMap内的value拼接）拼接名
     *
     * @param idValueMap id和编号or名称等的对应关系map（id类型是String）
     * @param idArr 关联的id数组，格式为[1, 2]
     * @return java.lang.String
     * @author 徐俊杰
     * @date 2019/12/6 9:10
     * @since v1.0
     */
    public String getRelationForStr(Map<String, String> idValueMap, JSONArray idArr) {
        if (idArr == null) {
            return "";
        }
        List<String> idIn = JsonHelperUtil.parseArray(idArr.toString(), String.class);
        StringBuilder str = new StringBuilder();
        for (String id : idIn) {
            String value = idValueMap.get(id);
            if (StringUtil.isEmpty(value)) {
                continue;
            }
            if (str.length() != 0) {
                str.append(",");
            }
            str.append(value);
        }
        return str.toString();
    }

    //-------------------------图表相关-------------------------

    /**
     * 过滤无法发布到首页的图表
     *
     * @param systemCode 系统图表
     * @return boolean 是否可以发布到首页(true无法发布,false可以发布)
     * @author 徐俊杰
     * @date 2019/5/6 16:19
     * @since v1.0
     */
    public boolean publishFilter(String systemCode, Integer chartType) {
        boolean filter = false;
        //自定义图独有的类型冲突(比如销售漏斗)
        boolean typeConflict = false;
        boolean customerStage = false;
        //如果systemCode为null,说明是自定义图表
        if (!StringUtil.isEmpty(systemCode)) {
            SystemCodeEnum systemCodeEnum = SystemCodeEnum.getByCode(systemCode);
            typeConflict = Objects.equals(systemCodeEnum, SystemCodeEnum.OPPORTUNITY_FUNNEL_CHART);
            customerStage = Objects.equals(systemCodeEnum, SystemCodeEnum.CUSTOMER_FUNNEL_CHART);
            if (customerStage) {
                return false;
            }
            List<Integer> filterConditionList = systemCodeEnum.getFilterConditionList();
            //包含首页无法筛选的模板或者是筛选时间不存在月份的筛选条件（首页默认筛选本月）
            filter =
                    filterConditionList.contains(FilterConditionEnum.CUSTOMER_TEMPLATE_NOALL.getCode()) ||
                            filterConditionList.contains(FilterConditionEnum.WORK_ORDER_TEMPLATE_NOALL.getCode()) ||
                            filterConditionList.contains(FilterConditionEnum.TIME_NO_MONTH.getCode()) ||
                            filterConditionList.contains(FilterConditionEnum.OPPORTUNITY_TEMPLATE_NO_ALL.getCode()) ||
                            filterConditionList.contains(FilterConditionEnum.AREA.getCode());
        }
        //表格、指标图无法发布到首页
        boolean ifTable = Objects.equals(chartType, ChartTypeEnum.TABLE.getCode()) ||
                Objects.equals(chartType, ChartTypeEnum.DATA_QUERY_TABLE.getCode()) ||
                Objects.equals(chartType, ChartTypeEnum.DATA_STATISTICS_TABLE.getCode()) || Objects.equals(chartType, ChartTypeEnum.TARGET_CHART.getCode()) ||
                Objects.equals(chartType, ChartTypeEnum.SECOND_TARGET_CHART.getCode());

        boolean ifMarket = Objects.equals(systemCode, SystemCodeEnum.CLUE_INVALID_RESOURCE.getSystemCode());
        return filter || ifTable || typeConflict || ifMarket;
    }

    //-------------------------从data内读取数据相关-------------------------

    /**
     * 格式化地址
     *
     * @param data        表单数据
     * @param addressAttr 地址字段名
     * @return java.lang.String
     * @author zcp
     * @date 2019/5/17 11:02
     * @version v1.0
     * @since v1.0
     */
    public String formatAddress(JSONObject data, String addressAttr) {
        JSONObject addressObj = data.getJSONObject(addressAttr);
        return formatAddress(addressObj);
    }

    /**
     * 格式化地址
     *
     * @param addressObj 地址存值对象
     * @return java.lang.String
     * @author zcp
     * @date 2019/7/3 19:15
     * @version v1.0
     * @since v1.0
     */
    public String formatAddress(JSONObject addressObj) {
        if (addressObj == null) {
            return "";
        }
        String address = addressObj.getOrDefault(PaasConstant.PROVINCE, "").toString() + addressObj.getOrDefault(PaasConstant.CITY, "").toString() + addressObj.getOrDefault(PaasConstant.DISTRICT, "").toString() + addressObj.getOrDefault(PaasConstant.ADDRESS, "").toString();
        return address;
    }

    public FieldAttrEntity getSubFormAttr(FieldAttrEntity fieldAttrEntity){
        if(Objects.nonNull(fieldAttrEntity)){
            SubFormPoJo subFormPoJo = fieldAttrEntity.getSubForm();
            List<? extends FieldAttrEntity> jsonArray = subFormPoJo.getItems();
            //TODO 这里是否可以直接取
            for(int i=0;i<jsonArray.size();i++){
                FieldAttrEntity fieldAttr = jsonArray.get(i);
                if(BasicConstant.TEXT_2.equals(fieldAttr.getAttr())){
                    return fieldAttr;
                }
            }
        }
        return null;
    }
    /**
     * 处理移动端联系人拨号
     *
     * @param phoneJsonArray 电话
     * @param name           显示名称
     * @param businessType   业务类型
     * @param dataId         数据id
     * @author 郑风
     * @update 2019/5/17 11:41 by zcp 从MobileDetailServiceImpl把逻辑抽出，保留原方法，内部调用该方法
     */
    public List<MobileDetailDialPhonePojo> handleDialPhone(JSONArray phoneJsonArray, String name, Integer businessType, Long dataId,HandlerExplainDTO handlerExplainDTO,FieldAttrEntity fieldAttrEntity, UserVO loginUser) throws XbbException {
        List<MobileDetailDialPhonePojo> mobileDetailDialPhonePojoList = new ArrayList<>();
        if (phoneJsonArray == null) {
            return mobileDetailDialPhonePojoList;
        }
        for (Object obj : phoneJsonArray) {
            JSONObject phoneObj = (JSONObject) obj;
            String type = phoneObj.getString(StringConstant.PHONE_TYPE_ATTR);
            String tel = phoneObj.getString(StringConstant.PHONE_TEL_ATTR);
            MobileDetailDialPhonePojo mobileDetailDialPhonePojo = new MobileDetailDialPhonePojo();
            mobileDetailDialPhonePojo.setName(Objects.isNull(name) ? PhoneExtEnum.getByCode(type).getName() : name);
            mobileDetailDialPhonePojo.setPhoneNum(tel);
            mobileDetailDialPhonePojo.setBusinessType(businessType);
            mobileDetailDialPhonePojo.setDataId(dataId);
            // todo 电话隐藏
            boolean isShow = false;
            if(Objects.nonNull(fieldAttrEntity) && Objects.nonNull(handlerExplainDTO)) {
                if(Objects.equals(fieldAttrEntity.getVisible(),BasicConstant.ONE)) {
                    isShow = ExplainUtil.invisibleOfScope(fieldAttrEntity, handlerExplainDTO);
                }else {
                    isShow = true;
                }
            }

            if (isShow) {
                String telNum;
                if (Objects.nonNull(tel) && tel.length() > 5) {
                    telNum = tel.substring(0, 3) + "****" + tel.substring(tel.length() - 2, tel.length());
                } else {
                    telNum = "*****";
                }
                mobileDetailDialPhonePojo.setTelNum(telNum);
            } else {
                UserVO currentLoginUser = Objects.nonNull(handlerExplainDTO) ? handlerExplainDTO.getLoginUser() : loginUser;
                boolean isAdmin = Objects.nonNull(currentLoginUser) && currentLoginUser.isAdminOrBoss();
                if (Objects.nonNull(fieldAttrEntity) && Objects.equals(fieldAttrEntity.getIsMaskTelephone(), 1) && !isAdmin) {
                    String maskTelephoneCode = TelephoneMaskUtil.maskTelephone(tel);
                    mobileDetailDialPhonePojo.setTelNum(maskTelephoneCode);
                } else {
                    mobileDetailDialPhonePojo.setTelNum(tel);
                }
            }
            mobileDetailDialPhonePojoList.add(mobileDetailDialPhonePojo);
        }
        return mobileDetailDialPhonePojoList;
    }

    /**
     * 处理移动端联系人拨号
     *
     * @param phoneJsonArray 电话
     * @param name           显示名称
     * @param businessType   业务类型
     * @param dataId         数据id
     * @param nameVisiable   名称可见
     * @author long.rao
     * @date 2019-10-21 16:56
     */
    public List<MobileDetailDialPhonePojo> handlePhoneForDetail(Boolean publicCanSeePhone, JSONArray phoneJsonArray, String name, Integer businessType, Long dataId,HandlerExplainDTO handlerExplainDTO,FieldAttrEntity fieldAttrEntity, boolean nameVisiable) throws XbbException {
        List<MobileDetailDialPhonePojo> mobileDetailDialPhonePojoList = new ArrayList<>();
        if (CollectionsUtil.isEmpty(phoneJsonArray)) {
            MobileDetailDialPhonePojo mobileDetailDialPhonePojo = new MobileDetailDialPhonePojo();
            if (nameVisiable) {
                mobileDetailDialPhonePojo.setName(name);
            }
            mobileDetailDialPhonePojo.setBusinessType(businessType);
            mobileDetailDialPhonePojo.setDataId(dataId);
            mobileDetailDialPhonePojoList.add(mobileDetailDialPhonePojo);
            return mobileDetailDialPhonePojoList;
        }
        for (Object obj : phoneJsonArray) {
            JSONObject phoneObj = (JSONObject) obj;
            String type = phoneObj.getString(StringConstant.PHONE_TYPE_ATTR);
            String tel = phoneObj.getString(StringConstant.PHONE_TEL_ATTR);
            MobileDetailDialPhonePojo mobileDetailDialPhonePojo = new MobileDetailDialPhonePojo();
            if (nameVisiable) {
                mobileDetailDialPhonePojo.setName(Objects.isNull(name) ? PhoneExtEnum.getByCode(type).getName() : name);
            }
            mobileDetailDialPhonePojo.setPhoneNum(tel);
            mobileDetailDialPhonePojo.setBusinessType(businessType);
            mobileDetailDialPhonePojo.setDataId(dataId);
            boolean phoneHide = false;
            if(Objects.nonNull(fieldAttrEntity) && Objects.nonNull(handlerExplainDTO)) {
                if(Objects.equals(fieldAttrEntity.getVisible(),BasicConstant.ONE)) {
                    phoneHide = ExplainUtil.invisibleOfScope(fieldAttrEntity, handlerExplainDTO);
                }else {
                    phoneHide = true;
                }
            }

            if (!publicCanSeePhone || phoneHide) {
                String telNum;
                if (Objects.nonNull(tel) && tel.length() > 5) {
                    telNum = tel.substring(0, 3) + "****" + tel.substring(tel.length() - 2, tel.length());
                } else {
                    telNum = "*****";
                }
                mobileDetailDialPhonePojo.setTelNum(telNum);
            } else {
                boolean isAdmin = Objects.nonNull(handlerExplainDTO) && Objects.nonNull(handlerExplainDTO.getLoginUser()) && handlerExplainDTO.getLoginUser().isAdminOrBoss();
                if (Objects.nonNull(fieldAttrEntity) && Objects.equals(fieldAttrEntity.getIsMaskTelephone(), 1) && !isAdmin) {
                    String maskTelephoneCode = TelephoneMaskUtil.maskTelephone(tel);
                    mobileDetailDialPhonePojo.setTelNum(maskTelephoneCode);
                } else {
                    mobileDetailDialPhonePojo.setTelNum(tel);
                }
            }
            mobileDetailDialPhonePojoList.add(mobileDetailDialPhonePojo);
        }
        return mobileDetailDialPhonePojoList;
    }

    //-------------------------封装数据相关-------------------------

    /**
     * 封装客户跟进记录list
     *
     * @param commnetCountMap  id--评论数
     * @param likeCountMap     id--点赞数
     * @param hasCommentIdList 我评论过的ids
     * @param hasLikeIdList    我点赞过的ids
     * @param entityExt        实体
     * @param data             实体内的data
     * @param customerId       客户id
     * @param customerName     客户名称
     * @param typeMap          code--跟进方式name
     * @param creatorId        创建人userId
     * @param userName         创建人名称
     * @param userAvatar       创建人头像
     * @return com.xbongbong.pro.weblist.pojo.CustomerCommunicatePojo
     * @throws XbbException
     * @author zcp
     * @date 2019/5/12 18:20
     * @version v1.0
     * @since v1.0
     */
    public CustomerCommunicatePojo setCustomerCommunicatePojo(Map<Long, Integer> commnetCountMap, Map<Long, Integer> likeCountMap, List<Long> hasCommentIdList, List<Long> hasLikeIdList, PaasFormDataEntityExt entityExt, JSONObject data, Long customerId, String customerName, Map<String, String> typeMap, String creatorId, String userName, Object userAvatar) throws XbbException {
        Long id = entityExt.getId();
        String type = typeMap.getOrDefault(data.getString(CustomerCommunicateEnum.TYPE.getAttr()), I18nMessageUtil.getMessage(StringConstant.VALUE_NOT_SET));
        JSONArray images = getJsonArrOrDefaultFromFormData(data, CustomerCommunicateEnum.IMAGES.getAttr(), new JSONArray());
        JSONArray contact = data.getJSONArray(CustomerCommunicateEnum.CONTACT_NAME_LINK_TEXT.getAttr());
        Integer communicateBuisenssType = data.getInteger(CommunicateBaseEnum.COMMUNICATE_BUSINESS_TYPE.getAttr());

        CustomerCommunicatePojo customerCommunicatePojo = new CustomerCommunicatePojo();
        customerCommunicatePojo.setId(id);
        customerCommunicatePojo.setCommunicateTime(DateUtil.getString(data.getInteger(CustomerCommunicateEnum.COMMUNICATE_TIME.getAttr()), DateUtil.SDFYMDHM));
        customerCommunicatePojo.setCustomerId(customerId);
        customerCommunicatePojo.setCustomerName(customerName);
        customerCommunicatePojo.setMemo(data.getString(CustomerCommunicateEnum.MEMO.getAttr()));
        customerCommunicatePojo.setType(type);
        if (!Objects.isNull(communicateBuisenssType)) {
            //方便前端跳转详情
            String businessName = XbbRefTypeEnum.getByCode(communicateBuisenssType).getName();
            customerCommunicatePojo.setBusinessType(data.getInteger(CustomerCommunicateEnum.COMMUNICATE_BUSINESS_TYPE.getAttr()));
            customerCommunicatePojo.setBusinessId(data.getLong(CustomerCommunicateEnum.COMMUNICATE_BUSINESS.getAttr()));
            customerCommunicatePojo.setBusinessName(businessName + PaasConstant.LEFT_BRACKET + data.getString(SupplierCommunicateEnum.COMMUNICATE_BUSINESS_LINK_TEXT.getAttr()) + PaasConstant.RIGHT_BRACKET);
        }
        customerCommunicatePojo.setUserId(creatorId);
        customerCommunicatePojo.setUserName(userName);
        customerCommunicatePojo.setUserAvatar(userAvatar);
        customerCommunicatePojo.setCommentCount(commnetCountMap.getOrDefault(id, 0));
        customerCommunicatePojo.setLikeCount(likeCountMap.getOrDefault(id, 0));
        customerCommunicatePojo.setImages(images);
        customerCommunicatePojo.setHasMyComment(hasCommentIdList.contains(id) ? 1 : 0);
        customerCommunicatePojo.setHasMyLike(hasLikeIdList.contains(id) ? 1 : 0);
        if (contact != null && contact.size() > 0) {
            customerCommunicatePojo.setContact(contact);
        }
        String communicateVideo = data.getString(CustomerCommunicateEnum.VIDEO.getAttr());
        if (!StringUtil.isEmpty(communicateVideo)) {
            customerCommunicatePojo.setIfVideo(true);
        }

        String communicateFile = data.getString(CustomerCommunicateEnum.FILE.getAttr());
        if (!StringUtil.isEmpty(communicateFile)) {
            customerCommunicatePojo.setIfFile(true);
        }
        String addressStr;
        JSONObject addressJsonObject = data.getJSONObject(CommunicateBaseEnum.ADDRESS.getAttr());
        if (Objects.nonNull(addressJsonObject)) {
            try {
                addressStr = addressJsonObject.getString(PaasConstant.PROVINCE) + addressJsonObject.getString(PaasConstant.CITY) + addressJsonObject.getString(PaasConstant.DISTRICT) + addressJsonObject.getString(PaasConstant.ADDRESS);
            } catch (Exception e) {
                addressStr = "";
            }
            customerCommunicatePojo.setAddress(addressStr);
        }
        //设置appId、menuId
        customerCommunicatePojo.setAppId(entityExt.getAppId());
        customerCommunicatePojo.setMenuId(entityExt.getMenuId());
        return customerCommunicatePojo;
    }

    /**
     * 封装客户跟进记录list
     *
     * @param commnetCountMap  id--评论数
     * @param likeCountMap     id--点赞数
     * @param hasCommentIdList 我评论过的ids
     * @param hasLikeIdList    我点赞过的ids
     * @param entityExt        实体
     * @param data             实体内的data
     * @param customerId       客户id
     * @param customerName     客户名称
     * @param creatorId        创建人userId
     * @param userName         创建人名称
     * @param userAvatar       创建人头像
     * @param explainMap       解释
     * @param contactAttrVisible 联系人是否可见
     * @return com.xbongbong.pro.weblist.pojo.CustomerCommunicatePojo
     * @throws XbbException
     * @author long.rao
     * @date 2019-08-02 18:51
     */
    public CustomerCommunicatePojo setCommunicatePojo(Map<Long, Integer> commnetCountMap, Map<Long, Integer> likeCountMap, List<Long> hasCommentIdList, List<Long> hasLikeIdList, PaasFormDataEntityExt entityExt, JSONObject data, Long customerId, String customerName, String creatorId, String userName, Object userAvatar, Map<String, FieldAttrEntity> explainMap, boolean contactAttrVisible, Integer distributorMark) throws XbbException {
        Long id = entityExt.getId();
        Object typeObj = data.get(CustomerCommunicateEnum.TYPE.getAttr());
        String type = null;
        if (Objects.nonNull(typeObj)) {
            type = ((ItemPoJo) typeObj).getText();
        }
        type = type == null ? I18nMessageUtil.getMessage(StringConstant.VALUE_NOT_SET) : type;
        JSONArray images = new JSONArray();
        try {
            images = getJsonArrOrDefaultFromFormData(data, CustomerCommunicateEnum.IMAGES.getAttr(), new JSONArray());
        } catch (Exception e) {
            LOG.error("commonHelp.setCommunicatePojo解析图片出错 corpid= " + entityExt.getCorpid(), e);
        }
        JSONArray contact = data.getJSONArray(CustomerCommunicateEnum.CONTACT_NAME_LINK_TEXT.getAttr());
        Integer communicateBuisenssType = data.getInteger(CommunicateBaseEnum.COMMUNICATE_BUSINESS_TYPE.getAttr());

        CustomerCommunicatePojo customerCommunicatePojo = new CustomerCommunicatePojo();
        customerCommunicatePojo.setId(id);
        customerCommunicatePojo.setCommunicateTime(DateUtil.getString(data.getInteger(CustomerCommunicateEnum.COMMUNICATE_TIME.getAttr()), DateUtil.SDFYMDHM));
        customerCommunicatePojo.setCustomerId(customerId);
        customerCommunicatePojo.setCustomerName(customerName);
        FieldAttrEntity attrEntity = explainMap.get(CustomerCommunicateEnum.MEMO.getAttr());
        if (attrEntity != null && Objects.equals(attrEntity.getVisible(), 1) && Objects.equals(attrEntity.getIsOpen(), 1)) {
            customerCommunicatePojo.setMemo(data.getString(CustomerCommunicateEnum.MEMO.getAttr()));
        }
        customerCommunicatePojo.setType(type);
        if (!Objects.isNull(communicateBuisenssType)) {
            //方便前端跳转详情
            String businessName = XbbRefTypeEnum.getByCode(XbbRefTypeEnum.transferBusinessType4Distributor(communicateBuisenssType, distributorMark)).getName();
            customerCommunicatePojo.setBusinessType(data.getInteger(CustomerCommunicateEnum.COMMUNICATE_BUSINESS_TYPE.getAttr()));
            customerCommunicatePojo.setBusinessId(data.getLong(CustomerCommunicateEnum.COMMUNICATE_BUSINESS.getAttr()));
            customerCommunicatePojo.setBusinessName(businessName + PaasConstant.LEFT_BRACKET + data.getString(SupplierCommunicateEnum.COMMUNICATE_BUSINESS_LINK_TEXT.getAttr()) + PaasConstant.RIGHT_BRACKET);
        }
        customerCommunicatePojo.setUserId(creatorId);
        customerCommunicatePojo.setUserName(userName);
        customerCommunicatePojo.setUserAvatar(userAvatar);
        customerCommunicatePojo.setCommentCount(commnetCountMap.getOrDefault(id, 0));
        customerCommunicatePojo.setLikeCount(likeCountMap.getOrDefault(id, 0));
        customerCommunicatePojo.setImages(images);
        customerCommunicatePojo.setHasMyComment(hasCommentIdList.contains(id) ? 1 : 0);
        customerCommunicatePojo.setHasMyLike(hasLikeIdList.contains(id) ? 1 : 0);
        if (contact != null && contact.size() > 0 && contactAttrVisible) {
            customerCommunicatePojo.setContact(contact);
        }
        String communicateVideo = data.getString(CustomerCommunicateEnum.VIDEO.getAttr());
        if (!StringUtil.isEmpty(communicateVideo)) {
            customerCommunicatePojo.setIfVideo(true);
        }

        JSONArray communicateFile = getJsonArrOrDefaultFromFormData(data, CustomerCommunicateEnum.FILE.getAttr(), new JSONArray());
        if (communicateFile.size() > 0) {
            customerCommunicatePojo.setIfFile(true);
        }
        // fix: 20935 【跟进记录】跟进记录的表单中将位置字段设置为不可见，手机签到后，跟进记录的详情中仍显示位置信息
        FieldAttrEntity fieldAttrEntity = explainMap.get(CustomerCommunicateEnum.ADDRESS.getAttr());
        if (Objects.nonNull(fieldAttrEntity) && Objects.equals(fieldAttrEntity.getVisible(), 0)) {
            data.remove(CustomerCommunicateEnum.ADDRESS.getAttr());
        }
        Object address = data.get(CustomerCommunicateEnum.ADDRESS.getAttr());
        if (Objects.nonNull(address)) {
            if (address instanceof String) {
                customerCommunicatePojo.setAddress(address.toString());
            } else {
                // 位置字段不可见时，该字段没有经过处理，就还是jsobject格式的
                customerCommunicatePojo.setAddress(mergeAddressData(data.getJSONObject(CustomerCommunicateEnum.ADDRESS.getAttr())));
            }
        }
        //设置appId、menuId
        customerCommunicatePojo.setAppId(entityExt.getAppId());
        customerCommunicatePojo.setMenuId(entityExt.getMenuId());
        return customerCommunicatePojo;
    }

    /**
     * 设置应收款的others信息
     *
     * @param others    应收款others对象
     * @param amountStr 金额
     * @param status    状态（名称）
     * @param isRed     是否红冲
     * @param isBad     是否坏账
     * @author zcp
     * @date 2019/5/13 21:24
     * @version v1.0
     * @since v1.0
     */
    public void setPaymentOthers(JSONObject others, String amountStr, String status, Integer isRed, Integer isBad) {
        others.put("amount", amountStr);
        others.put("status", status);
        others.put("isRed", isRed);
        others.put("isBad", isBad);
    }

    /**
     * 资金这边单据的下拉框状态字段被转成了ItemPojo，这边转成文本字段，给前端展示
     * @param data 数据
     * @param attr
     * @return
     */
    public String formatFundStatusText(JSONObject data,String attr,String defaultStatus){
        try {
            Object statusItemPojoObj = data.get(attr);
            if (Objects.isNull(statusItemPojoObj)) {
                return defaultStatus;
            }
            if (statusItemPojoObj instanceof ItemPoJo) {
                return ((ItemPoJo)statusItemPojoObj).getText();
            }else {
                return defaultStatus;
            }
        }catch (Exception e){
            return defaultStatus;
        }
    }

    /**
     * 封装联系人的电话信息
     *
     * @param others             联系人others对象
     * @param contactPhoneArray  电话对象
     * @param isActiveCall       是否为话务中心模块
     * @param contactAttrVisible 电话是否可见
     * @param entity             联系人实体
     * @param data               实体内的data
     * @author zcp
     * @date 2019/5/14 14:03
     * @version v1.0
     * @since v1.0
     */
    public void setContactPhone(JSONObject others, JSONArray contactPhoneArray, boolean isActiveCall, Integer contactAttrVisible, PaasFormDataEntityExt entity, JSONObject data) throws XbbException {
        if (isActiveCall) {
            //前端用于判断呼叫中心相关
            others.put("isCallCenter", true);
            if (contactPhoneArray == null) {
                return;
            }
            JSONArray contactPhone = new JSONArray();
            for (int i = 0; i < contactPhoneArray.size(); i++) {
                JSONObject contactObj = new JSONObject();
                JSONObject item = contactPhoneArray.getJSONObject(i);
                contactObj.put("id", entity.getDataId());
                contactObj.put("refType", XbbRefTypeEnum.CONTACT.getCode());
                contactObj.put("name", data.getOrDefault(ContactEnum.NAME.getAttr(), ""));
                String phoneTel = item.getString(StringConstant.PHONE_TEL_ATTR);
                contactObj.put("telNum", phoneTel);
                if (!StringUtil.isEmpty(phoneTel)) {
                    contactObj.put("phoneNum", phoneTel);
                    if (contactAttrVisible == 0) {
                        String telNum = "";
                        if (phoneTel.length() > 5) {
                            telNum = phoneTel.substring(0, 3) + "****" + phoneTel.substring(phoneTel.length() - 2);
                        } else {
                            telNum = StringConstant.INVISIBLE_VALUE;
                        }
                        contactObj.put("telNum", telNum);
                    }
                } else {
                    contactObj.put("phoneNum", "");
                }
                contactPhone.add(contactObj);
            }

            if (!contactPhone.isEmpty()) {
                others.put("contactDial", contactPhone);
            }
        }
    }

    /**
     * 获取发票抬头
     *
     * @param customerInvoiceArr 发票信息（在发票实体data内作为子表单存放）
     * @return java.lang.String
     * @author zcp
     * @date 2019/5/16 10:10
     * @version v1.0
     * @since v1.0
     */
    public String getInvoiceTitle(JSONArray customerInvoiceArr) {
        if (customerInvoiceArr == null) {
            return "";
        }
        JSONObject customerInvoiceObj;
        try {
            customerInvoiceObj = customerInvoiceArr.getJSONObject(BasicConstant.ZERO);
        } catch (Exception e) {
            //throw new XbbException(SystemErrorCodeEnum.API_ERROR_100015);
            customerInvoiceObj = new JSONObject();
        }
        String title = "";
        if (customerInvoiceObj != null) {
            title = customerInvoiceObj.getString(CustomerInvoiceEnum.TITLE.getAttr());
            title = title == null ? "" : title;
        }
        return title;
    }

    /**
     * 设置发票信息
     *
     * @param data        数据data
     * @param superScript 角标
     * @param others      其他
     * @author zcp
     * @date 2019/5/16 10:57
     * @version v1.0
     * @since v1.0
     */
    public void setInvoiceInfo(JSONObject data, JSONObject superScript, JSONObject others) {
        // 角标
        Integer hasRed = data.getIntValue(InvoiceEnum.HAS_RED.getAttr());
        superScript.put("hasRed", hasRed);
        // 其他
        // TODO 小数精度
        String amount = data.getString(InvoiceEnum.TOTAL_MONEY.getAttr());
        others.put("amount", amount + UnitEnum.MONEY.getName());
        Integer isCancel = data.getIntValue(InvoiceEnum.IS_CANCEL.getAttr());
        others.put("isCancel", isCancel);
        Integer isRed = data.getIntValue(InvoiceEnum.IS_RED.getAttr());
        others.put("isRed",isRed);
    }

    public void setPurchaseInfo(JSONObject data,JSONObject superScript,JSONObject others){
        // 角标
        Integer hasRed = data.getIntValue(PurchaseInvoiceEnum.HAS_RED.getAttr());
        superScript.put("hasRed", hasRed);
        // 其他
        // TODO 小数精度
        String amount = data.getString(PurchaseInvoiceEnum.TOTAL_MONEY.getAttr());
        others.put("amount", amount + UnitEnum.MONEY.getName());
        Integer isCancel = data.getIntValue(PurchaseInvoiceEnum.IS_CANCEL.getAttr());
        others.put("isCancel", isCancel);
        Integer isRed = data.getIntValue(PurchaseInvoiceEnum.IS_RED.getAttr());
        others.put("isRed",isRed);
    }

    /**
     * 设置进项发票信息
     *
     * @param data 数据data
     * @param superScript 角标
     * @param others 其他
     * @author 徐俊杰
     * @date 2020/3/16 15:48
     * @since v1.0
     */
    public void setInvoicePurchaseInfo(JSONObject data, JSONObject superScript, JSONObject others) {
        // 角标
        Integer hasRed = data.getIntValue(PurchaseInvoiceEnum.HAS_RED.getAttr());
        superScript.put("hasRed", hasRed);
        // 其他
        // TODO 小数精度
        String amount = data.getString(PurchaseInvoiceEnum.TOTAL_MONEY.getAttr());
        others.put("amount", amount + UnitEnum.MONEY.getName());
        Integer isCancel = data.getIntValue(PurchaseInvoiceEnum.IS_CANCEL.getAttr());
        others.put("isCancel", isCancel);
        Integer isRed = data.getIntValue(PurchaseInvoiceEnum.IS_RED.getAttr());
        others.put("isRed",isRed);
    }

    /**
     * 格式化子表单（发票信息）内开关、文本，返回字符串形式
     *
     * @param value 数据对象
     * @return java.lang.String
     * @author zcp
     * @date 2019/7/3 22:18
     * @version v1.0
     * @since v1.0
     */
    public String formatInvoice(Object value) {
        if (value == null) {
            return "";
        }
        CustomerInvoiceEnum titleTypeEnum = CustomerInvoiceEnum.TITLE_TYPE;
        CustomerInvoiceEnum titleEnum = CustomerInvoiceEnum.TITLE;
        CustomerInvoiceEnum taxpayerNoEnum = CustomerInvoiceEnum.TAXPAYER_NO;
        CustomerInvoiceEnum registeredPhoneEnum = CustomerInvoiceEnum.REGISTERED_PHONE;
        CustomerInvoiceEnum invoiceAddressEnum = CustomerInvoiceEnum.INVOICE_ADDRESS;
        CustomerInvoiceEnum bankEnum = CustomerInvoiceEnum.BANK;
        CustomerInvoiceEnum accountEnum = CustomerInvoiceEnum.ACCOUNT;
        StringBuilder sb = new StringBuilder();
        JSONArray valueArr = JsonHelperUtil.parseArray(value.toString());
        valueArr.forEach(obj -> {
            if (sb.length() > 0) {
                sb.append(SymbolConstant.SEMICOLON_CHINESE);
            }
            JSONObject item = (JSONObject) obj;
            String titleTypeValue = item.getString(titleTypeEnum.getAttr());
            String titleType = I18nMessageUtil.getMessage(InvoiceTitleTypeEnum.getNameByValue(titleTypeValue));
            sb.append(titleTypeEnum.getName()).append(SymbolConstant.COLON_ENGLISH).append(titleType).append(SymbolConstant.SEMICOLON_ENGLISH)
                    .append(titleEnum.getName()).append(SymbolConstant.COLON_ENGLISH).append(item.getString(titleEnum.getAttr())).append(SymbolConstant.SEMICOLON_ENGLISH)
                    .append(taxpayerNoEnum.getName()).append(SymbolConstant.COLON_ENGLISH).append(item.getString(taxpayerNoEnum.getAttr())).append(SymbolConstant.SEMICOLON_ENGLISH)
                    .append(registeredPhoneEnum.getName()).append(SymbolConstant.COLON_ENGLISH).append(item.getString(registeredPhoneEnum.getAttr())).append(SymbolConstant.SEMICOLON_ENGLISH)
                    .append(invoiceAddressEnum.getName()).append(SymbolConstant.COLON_ENGLISH).append(item.getString(invoiceAddressEnum.getAttr())).append(SymbolConstant.SEMICOLON_ENGLISH)
                    .append(bankEnum.getName()).append(SymbolConstant.COLON_ENGLISH).append(item.getString(bankEnum.getAttr())).append(SymbolConstant.SEMICOLON_ENGLISH)
                    .append(accountEnum.getName()).append(SymbolConstant.COLON_ENGLISH).append(item.getString(accountEnum.getAttr()));
        });
        return sb.toString();
    }

    /**
     * 格式化子表单（寄送信息）内开关、文本，返回字符串形式
     *
     * @param value 数据对象
     * @return java.lang.String
     * @author zcp
     * @date 2019/7/3 22:18
     * @version v1.0
     * @since v1.0
     */
    public String formatInvoiceExpress(Object value) {
        if (value == null) {
            return "";
        }
        CustomerExpressEnum nameEnum = CustomerExpressEnum.NAME;
        CustomerExpressEnum contactEnum = CustomerExpressEnum.CONTACT;
        CustomerExpressEnum phoneEnum = CustomerExpressEnum.PHONE;
        CustomerExpressEnum zipCodeEnum = CustomerExpressEnum.ZIP_CODE;
        CustomerExpressEnum addressEnum = CustomerExpressEnum.ADDRESS;
        StringBuilder sb = new StringBuilder();
        JSONArray valueArr = JsonHelperUtil.parseArray(value.toString());
        valueArr.forEach(obj -> {
            if (sb.length() > 0) {
                sb.append(SymbolConstant.SEMICOLON_CHINESE);
            }
            JSONObject item = (JSONObject) obj;
            JSONObject addressObj = item.getJSONObject(addressEnum.getAttr());
            String address = formatAddress(addressObj);
            sb.append(nameEnum.getName()).append(SymbolConstant.COLON_ENGLISH).append(item.getString(nameEnum.getAttr())).append(SymbolConstant.SEMICOLON_ENGLISH)
                    .append(contactEnum.getName()).append(SymbolConstant.COLON_ENGLISH).append(item.getString(contactEnum.getAttr())).append(SymbolConstant.SEMICOLON_ENGLISH)
                    .append(phoneEnum.getName()).append(SymbolConstant.COLON_ENGLISH).append(item.getString(phoneEnum.getAttr())).append(SymbolConstant.SEMICOLON_ENGLISH)
                    .append(zipCodeEnum.getName()).append(SymbolConstant.COLON_ENGLISH).append(item.getString(zipCodeEnum.getAttr())).append(SymbolConstant.SEMICOLON_ENGLISH)
                    .append(addressEnum.getName()).append(SymbolConstant.COLON_ENGLISH).append(address);
        });
        return sb.toString();
    }

    /**
     * @param entityExt    退货退款实体
     * @param data         实体内的data
     * @param customerName 客户名称
     * @param signUserStr  签订人、or分配人，拼接过“签订人:”或“归属人(所占比例):”
     * @param explains     解释
     * @param others       其他
     * @author zcp
     * @date 2019/5/16 20:24
     * @version v1.0
     * @since v1.0
     */
    public void setRefundInfo(PaasFormDataEntityExt entityExt, JSONObject data, String customerName, String signUserStr, JSONArray explains, JSONObject others) {
        Long time = data.getLongValue(RefundEnum.SIGN_DATE.getAttr());
        String signTimeStr = time == 0L ? "" : DateTimeUtil.getStringEpochSecond(time, DateTimeUtil.SDFDate);
        // 解释
        // 退货退款合同名称
        String name = (String) data.getOrDefault(RefundEnum.REFUND_NAME.getAttr(), "");
        explains.add(ContractEnum.NAME.getAttrName() + ":" + name);
        explains.add(signUserStr);
        // 签订日期
        String refundSignDate = RefundEnum.SIGN_DATE.getAttrName() + ":";
        explains.add(refundSignDate + signTimeStr);
        // 客户
        String refundLinkedText = RefundEnum.CUSTOMER_ID.getAttrName() + ":";
        explains.add(refundLinkedText + customerName);
        //其他
        others.put("serialNo", entityExt.getSerialNo());
        // 退货金额
        String amount = StringUtil.formatDouble(data.getDoubleValue(RefundEnum.RETURN_AMOUNT.getAttr()));
        others.put("amount", amount);
    }


    //-----------------------------private方法

    /**
     * 低于一小时的转为分钟
     *
     * @param hours 小时
     * @return 分钟
     * @version 3.13
     * @author zheng.li
     * 添加时间 2017-11-28 上午10:02:04
     * 修改时间 2017-11-28 上午10:02:04
     * @since 3.13
     */
    private static Integer hourToMinute(Double hours) {
        if (hours == null) {
            return 0;
        }

        MathContext mc = new MathContext(2);
        BigDecimal minutesDecimal = BigDecimal.valueOf(hours * 60).round(mc);

        return minutesDecimal.intValue();
    }

    /**
     * 设置摘要
     *
     * @param attrName
     * @param value
     * @return SummaryDataPoJo
     * @author long.rao
     * @date 2019-06-25 14:00
     */
    public SummaryDataPoJo setSummary(String attrName, String value) {
        if (value == null) {
            value = "";
        }
        SummaryDataPoJo summary = new SummaryDataPoJo();
        summary.setAttrName(attrName);
        summary.setValue(Collections.singletonList(value));
        return summary;
    }

    /**
     * 设置标签
     *
     * @param attrName
     * @param value
     * @return SummaryDataPoJo
     * @author long.rao
     * @date 2019-06-25 14:00
     */
    public SummaryDataPoJo setLable(String attrName, String value) {
        if (value == null) {
            value = "";
        }
        SummaryDataPoJo lable = new SummaryDataPoJo();
        lable.setAttrName(attrName);
        lable.setValue(Collections.singletonList(value));
        return lable;
    }

    /**
     * 设置标题
     *
     * @param value
     * @return FormTitlePoJo
     * @author long.rao
     * @date 2019-06-25 14:00
     */
    public FormTitlePoJo setTitle(String value) {
        if (value == null) {
            value = "";
        }
        FormTitlePoJo title = new FormTitlePoJo();
        title.setValue(Collections.singletonList(value));
        return title;
    }

    /**
     * 格式化子表单内开关、文本，返回字符串形式
     *
     * @param valueArr 数据对象
     * @return java.lang.String
     * @author zcp
     * @date 2019/7/3 22:18
     * @version v1.0
     * @since v1.0
     */
    public String formatSwitchText(Object valueArr) {
        if (valueArr == null) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        JSONArray jsonArray = JsonHelperUtil.parseArray(valueArr.toString());
        String attrContent = WorkReportPlanEnum.CONTENT.getAttr();
        String attrStatus = WorkReportPlanEnum.STATUS.getAttr();
        for (int i = 0; i < jsonArray.size(); i++) {
            if (sb.length() > 0) {
                sb.append(SymbolConstant.SEMICOLON_CHINESE);
            }
            JSONObject item = (JSONObject) jsonArray.get(i);
            int index = i + 1;
            String content = item.getString(attrContent);
            int enable = item.getIntValue(attrStatus);
            String status = enable == BasicConstant.ONE ? String.format(SymbolConstant.BRACKETS_ENGLISH, RaidersFinishEnum.FINISH.getName()) : String.format(SymbolConstant.BRACKETS_ENGLISH, RaidersFinishEnum.FINISH_UN.getName());
            sb.append(index).append(status).append(SymbolConstant.POINT_ENGLISH).append(content);
        }
        return sb.toString();
    }

    /**
     * 把数组类型的对象，转为用逗号拼接的字符串格式
     * 如，把["张三","李四"]，变为-“张三,李四”
     *
     * @param valueArr 格式为["张三","李四"]
     * @return java.lang.String
     * @author zcp
     * @date 2019/7/4 21:08
     * @version v1.0
     * @since v1.0
     */
    public String changeArray2String(Object valueArr) {
        if (valueArr == null) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        JSONArray jsonArray;
        if (valueArr instanceof JSONArray) {
            jsonArray = (JSONArray) valueArr;
        } else if (valueArr instanceof List) {
            jsonArray = JsonHelperUtil.parseArray(JSON.toJSONString(valueArr));
        } else {
            jsonArray = JsonHelperUtil.parseArray(valueArr.toString());
        }
        jsonArray.forEach(item -> {
            if (sb.length() > 0) {
                sb.append(SymbolConstant.COMMA_ENGLISH);
            }
            sb.append(item);
        });
        return sb.toString();
    }

    /**
     * 把数组类型的对象，转为用逗号拼接的字符串格式（支持拼接对象内的某个特定key）
     * 如，当valueArr为[{"name": "张三", "age": 10}, {"name": "名字2", "age": 10}]，key为“name”时，则处理后变为-“张三,名字2”
     *
     * @param valueArr 数据对象，格式为[{"name": "张三", "age": 10}, {"name": "名字2", "age": 10}]
     * @param key      获取数据对象内某个key的值进行拼装
     * @return java.lang.String
     * @author zcp
     * @date 2019/7/6 17:14
     * @version v1.0
     * @since v1.0
     */
    public String changeArray2String(Object valueArr, String key) {
        if (valueArr == null) {
            return "";
        }
        JSONArray jsonArray = JsonHelperUtil.parseArray(valueArr.toString());
        return changeArray2String(jsonArray, key);
    }

    /**
     * 把数组类型的对象，转为用逗号拼接的字符串格式（支持拼接对象内的某个特定key）
     * 如，当valueArr为[{"name": "张三", "age": 10}, {"name": "名字2", "age": 10}]，key为“name”时，则处理后变为-“张三,名字2”
     *
     * @param jsonArray 数据对象，格式为[{"name": "张三", "age": 10}, {"name": "名字2", "age": 10}]
     * @param key       获取数据对象内某个key的值进行拼装
     * @return java.lang.String
     * @author zcp
     * @date 2019/7/8 19:28
     * @version v1.0
     * @since v1.0
     */
    public String changeArray2String(JSONArray jsonArray, String key) {
        StringBuilder sb = new StringBuilder();
        jsonArray.forEach(item -> {
            if (sb.length() > 0) {
                sb.append(SymbolConstant.COMMA_ENGLISH);
            }
            JSONObject jsonObject = (JSONObject) item;
            sb.append(jsonObject.get(key));
        });
        return sb.toString();
    }

    /**
     * 判断客户阶段字段是否开启
     *
     * @param businessType 业务类型
     * @param explainMap 字段解释
     * @param handlerExplainDTO 创建人、负责人、协同人等信息
     * @return boolean 客户阶段字段是否开启
     * @author long.rao
     * @date 2019-07-09 10:37
     */
    public static boolean ifStageOpen(Integer businessType, Map<String, FieldAttrEntity> explainMap, HandlerExplainDTO handlerExplainDTO) {
        String attr = "";
        if (Objects.equals(businessType, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())) {
            attr = CustomerManagementEnum.CUSTOMER_STAGE.getAttr();
        } else if (Objects.equals(businessType, XbbRefTypeEnum.SALES_OPPORTUNITY.getCode())) {
            attr = SalesOpportunityEnum.SALE_STAGE.getAttr();
        } else if (Objects.equals(businessType, XbbRefTypeEnum.CLUE.getCode())) {
            attr = ClueEnum.CLUE_STAGE.getAttr();
        } else if (Objects.equals(businessType, XbbRefTypeEnum.CONTRACT.getCode())) {
            for (String fieldAttr : explainMap.keySet()) {
                FieldAttrEntity fieldAttrEntity = explainMap.get(fieldAttr);
                if (Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.STAGE_THRUSTER.getType())) {
                    attr = fieldAttr;
                    break;
                }
            }
        }
        FieldAttrEntity stage = explainMap.get(attr);
        if (stage == null) {
            return false;
        }
        boolean flag;
        // 可见及高级可见
        try {
            flag = !ExplainUtil.isFieldNeedHide(stage, handlerExplainDTO);
        } catch (XbbException e) {
            flag = false;
        }
        if (Objects.equals(stage.getIsOpen(), 1) && flag) {
            flag = true;
        } else {
            flag = false;
        }
        return flag;
    }

    /**
     * double 转成百分数
     *
     * @param d
     * @param integerDigits
     * @param fractionDigits
     * @return String
     */
    public String getPercentFormat(Double d, Integer integerDigits, Integer fractionDigits) {
        NumberFormat nf = NumberFormat.getPercentInstance();
        // 小数点前保留几位
        nf.setMaximumIntegerDigits(integerDigits);
        // 小数点后保留几位
        nf.setMinimumFractionDigits(fractionDigits);
        String str = nf.format(d);
        return str;
    }
    /**
     * double 转成百分数
     *
     * @param d
     * @param fractionDigits
     * @return String
     */
    public String getPercentFormat2(Double d,  Integer fractionDigits) {
        NumberFormat nf = NumberFormat.getPercentInstance();
        // 小数点后保留几位
        nf.setMinimumFractionDigits(fractionDigits);
        String str = nf.format(d);
        return str;
    }

    /**
     * 判断字段是否启用且必填
     *
     * @param explainMap
     * @return boolean
     * @author long.rao
     * @date 2019-07-16 16:16
     */
    public boolean attrEnable(String attr, Map<String, FieldAttrEntity> explainMap) {
        FieldAttrEntity attrEntity = explainMap.get(attr);
        if (attrEntity == null) {
            return false;
        }
        if (Objects.equals(attrEntity.getIsOpen(), 1) && Objects.equals(attrEntity.getVisible(), 1)) {
            return true;
        }
        return false;
    }

    /**
     * 字段可见
     * @param fieldAttrEntity 字段
     * @param dataId 数据id
     * @param creatorId 创建人
     * @param loginUser 登录人
     * @param corpid 公司id
     * @param businessType 业务类型
     * @return 是否可见 true可见；flase不可见
     */
    public Boolean attrCanSee(FieldAttrEntity fieldAttrEntity, Long dataId, String creatorId, UserVO loginUser, String corpid, int businessType) {
        if (fieldAttrEntity == null) {
            return false;
        }
        // 是否启用字段
        if (!Objects.equals(fieldAttrEntity.getIsOpen(), 1) || !Objects.equals(fieldAttrEntity.getVisible(), 1)) {
            return false;
        }
        //协同团队
        Map<Long, List<String>> mainUserMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, List<String>> coUserMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (Objects.isNull(dataId)){
            userTeamHelp.setUserIdMap(Collections.singletonList(dataId), corpid, businessType, false, mainUserMap, coUserMap);
        }
        // 当前数据的创建人、负责人、协同人、当前操作人
        HandlerExplainDTO handlerExplainDTO = new HandlerExplainDTO();
        handlerExplainDTO.setLoginUser(loginUser);
        handlerExplainDTO.setOwnerId(mainUserMap.get(dataId));
        handlerExplainDTO.setCoUserId(coUserMap.get(dataId));
        handlerExplainDTO.setCreatorId(creatorId);
        handlerExplainDTO.setUserId(loginUser.getUserId());
        try {
            // 判断是否可见
            return !ExplainUtil.invisibleOfScope(fieldAttrEntity, handlerExplainDTO);
        } catch (Exception e) {
            LOG.error("判断字段是否启用且(高级)可见出错 commonHelp.attrCanSee:" );
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 判断字段是否启用且必填且高级规则可见
     *
     * @param explainMap
     * @return boolean
     * @author long.rao
     * @date 2019-09-25 09:54
     */
    public boolean attrCanSee(String attr, Map<String, FieldAttrEntity> explainMap, HandlerExplainDTO handlerExplainDTO) {
        if (Objects.isNull(explainMap)) {
            return false;
        }
        FieldAttrEntity fieldAttrEntity = explainMap.get(attr);
        if (fieldAttrEntity == null) {
            return false;
        }

        // 是否启用字段
        if (!Objects.equals(fieldAttrEntity.getIsOpen(), 1) || !Objects.equals(fieldAttrEntity.getVisible(), 1)) {
            return false;
        }

        try {
            // 判断是否可见
            return !ExplainUtil.invisibleOfScope(fieldAttrEntity, handlerExplainDTO);
        } catch (Exception e) {
            LOG.error("判断字段是否启用且(高级)可见出错 commonHelp.attrCanSee:" + attr, e);
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 判断字段是否启用且必填且高级规则可见
     *
     * @param explainMap
     * @return boolean
     * @author long.rao
     * @date 2019-09-25 09:54
     */
    public boolean attrCanEdit(String attr, Map<String, FieldAttrEntity> explainMap, HandlerExplainDTO handlerExplainDTO) {
        boolean fieldCanEdit = false;
        FieldAttrEntity fieldAttrEntity = explainMap.get(attr);
        if (fieldAttrEntity == null) {
            return fieldCanEdit;
        }

        // 是否启用字段
        if (!Objects.equals(fieldAttrEntity.getIsOpen(), 1)) {
            return fieldCanEdit;
        }

        try {
            // 判断是否可编辑
            fieldCanEdit = ExplainUtil.isEditableOfScope(fieldAttrEntity, handlerExplainDTO);

        } catch (Exception e) {
            LOG.error("判断字段是否可编辑出错 attrCanEdit:" );
            e.printStackTrace();
        }
        return fieldCanEdit;
    }

    /**
     * 给人员控件没传userName的兼容一下
     *
     * @param corpid 公司id
     * @param ownerId 负责人ids
     * @param coUserId 协同人ids
     */
    public void setUserName4UserPojo(String corpid, List<ReturnUserAndDepartmentPojo> ownerId, List<ReturnUserAndDepartmentPojo> coUserId) {
        if (CollectionUtils.isNotEmpty(ownerId) && StringUtil.isEmpty(ownerId.get(0).getName())) {
            List<String> ownerUserIds = new ArrayList<>();
            for (ReturnUserAndDepartmentPojo returnUserAndDepartmentPojo : ownerId) {
                ownerUserIds.add(returnUserAndDepartmentPojo.getId().toString());
            }
            for (ReturnUserAndDepartmentPojo returnUserAndDepartmentPojo : coUserId) {
                ownerUserIds.add(returnUserAndDepartmentPojo.getId().toString());
            }
            Map<String, String> userIdNameMap = userModel.getUserIdNameMap(corpid, ownerUserIds);
            for (ReturnUserAndDepartmentPojo returnUserAndDepartmentPojo : ownerId) {
                returnUserAndDepartmentPojo.setName(userIdNameMap.getOrDefault(returnUserAndDepartmentPojo.getId().toString(), ""));
            }
            for (ReturnUserAndDepartmentPojo returnUserAndDepartmentPojo : coUserId) {
                returnUserAndDepartmentPojo.setName(userIdNameMap.getOrDefault(returnUserAndDepartmentPojo.getId().toString(), ""));
            }
        }
    }

    /**
     * 设置默认值
     *
     * @param attr 字段attr
     * @param value 字段的默认值
     * @return DefaultDataPojo 默认值的pojo
     * @author long.rao
     * @date 2019-08-26 16:51
     */
    public DefaultDataPojo setDefaultData(String attr, List<Object> value) {
        DefaultDataPojo productTotalDefault = new DefaultDataPojo();
        productTotalDefault.setValue(value);
        productTotalDefault.setAttr(attr);
        return productTotalDefault;
    }

    /**
     * 删除时，关联业务信息的封装
     * @param sheetAttrPojo 回款单/付款单的字段attr pojo
     * @param entityExt 回/付款单实体
     * @param sheetDeleteHandleRelateDTO 删除/批量删除回/付款单，更新其关联业务（客户、合同、应收款，或供应商、采购合同、付款计划）dto
     * @throws XbbException
     * @author zcp
     * @date 19/12/6 006 16:10
     * @update 20/3/17 by zcp 更改使其同时处理回款单、付款单，并优化相关代码
     * @since v1.0
     * @version v1.0
     */
    public void setPaymentSheetDeleteHandleRelateDTO(SheetAttrPojo sheetAttrPojo, PaasFormDataEntityExt entityExt, SheetDeleteHandleRelateDTO sheetDeleteHandleRelateDTO) throws XbbException {
        JSONObject data = JsonHelperUtil.getJSONObject(entityExt.getData());
        //回款单id-回款单信息pojo
        Map<Long, SheetInfoPojo> sheetInfoMap = sheetDeleteHandleRelateDTO.getSheetInfoMap();
        XbbRefTypeEnum xbbRefTypeEnum = sheetAttrPojo.getXbbRefTypeEnum();
        //预收/付款核销需要读取核销金额
        String sheetType;
        Double amount;
        switch (xbbRefTypeEnum) {
            case PAY_SHEET:
                sheetType = data.getString(PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr());
                amount = FundUtil.getSheetAmount4PaySheet(data, sheetType);
                break;
            default:
                sheetType = data.getString(PaySheetEnum.PAY_SHEET_TYPE.getAttr());
                amount = FundUtil.getSheetAmount4PaymentSheet(data, sheetType);
                break;
        }
        /*
         基于updateRelateList处理关联业务
         （1）对合同/应收款的已收、未收、坏账影响
         已核销、预收款核销：已收减少，未收增加
         红冲已核销、红冲预收款核销：已收增加，未收减少
         坏账：未收增加，坏账金额减少
         （2）对客户余额
         预收款：减少
         预收款核销、红冲预收款余额：增加
         */
        SheetTypeEnum sheetTypeEnum = SheetTypeEnum.getByCodeRefTypeEnum(sheetType, xbbRefTypeEnum);
        SheetInfoPojo sheetInfoPojo = new SheetInfoPojo(entityExt.getSerialNo(), data, amount, sheetType, sheetTypeEnum, entityExt.getAlone(), entityExt.getUuid());
        sheetInfoMap.put(entityExt.getId(), sheetInfoPojo);
        //默认：未收 + amount，已收 - amount
        Map<Long, Double> cidToAmountAdd = sheetDeleteHandleRelateDTO.getContractIdToUnAmount();
        Map<Long, Double> pidToAmountAdd = sheetDeleteHandleRelateDTO.getPaymentIdToUnAmount();
        Map<Long, Double> cidToAmountSub = sheetDeleteHandleRelateDTO.getContractIdToAmount();
        Map<Long, Double> pidToAmountSub = sheetDeleteHandleRelateDTO.getPaymentIdToAmount();
        boolean customerBalanceFlag = false;
        //true 更新合同/应收款的已收、未收 false 不需要更新合同/应收款的已收、未收
        boolean cpFlag = true;
        switch (sheetTypeEnum) {
            case WRITE_OFF:
            case RED_WRITE_OFF:
            case RED_WRITE_OFF_PREPAYMENT:
            case WRITE_OFF_PAY:
            case RED_WRITE_OFF_PAY:
            case RED_WRITE_OFF_PREPAY:
                /*
                 已核销：未收 + amount，已收 - amount
                 红冲已核销、红冲预收款核销：已收 + |amount|，未收 - |amount|（红冲的amount是负数，因此要对amount取反一下，则相当于：已收-amount，未收+amount，与已核销操作一致）
                 */
                break;
            case RED_WRITE_OFF_BALANCE:
                /*
                 红冲预收款核销(退到余额)：
                    已收 + |amount|，未收 - |amount|（红冲的amount是负数，因此要对amount取反一下，则相当于：已收-amount，未收+amount，与已核销操作一致）
                    客户余额：减少（本身就是负数，不用取反）
                 */
                customerBalanceFlag = true;
                break;
            case PREPAYMENT:
            case RED_PREPAYMENT:
            case PREPAY:
            case RED_PREPAY:
                /*
                预收款：客户余额 - amount
                红冲预收款：客户余额 + amount（红冲预收款余额的amount是负数，因此要取反一下，相当于客户余额 - amount）
                 */
                customerBalanceFlag = true;
                //预收款、红冲预收款不需要更新合同/应收款的已收、未收
                cpFlag = false;
                amount = Arith.mul(amount, -1);
                break;
            case WRITE_OFF_PREPAYMENT:
            case WRITE_OFF_PREPAY:
                //预收款核销：未收 + amount，已收 - amount，客户余额 + amount
                customerBalanceFlag = true;
                break;
            case BAD_PAYMENT:
            case BAD_PAY:
                //坏账：未收 + amount，坏账 - amount
                cidToAmountSub = sheetDeleteHandleRelateDTO.getContractIdToBad();
                pidToAmountSub = sheetDeleteHandleRelateDTO.getPaymentIdToBad();
                break;
            default:
                break;
        }
        if (customerBalanceFlag) {
            //只有预收款、红冲预收款、预收款核销、红冲预收款核销(退到余额)才会更新客户余额
            formatRelateCustomerBalance(sheetAttrPojo, data, sheetDeleteHandleRelateDTO.getCustomerIdToBalance(), amount);
        }
        if (cpFlag) {
            //预收款、红冲预收款不更新合同的已收/未收/坏账
            formatRelateAmount(data, entityExt, sheetAttrPojo.getAttrCon(), cidToAmountAdd, cidToAmountSub, sheetDeleteHandleRelateDTO.getContractIds(), amount, sheetDeleteHandleRelateDTO.getCidSheetIdListMap(), sheetDeleteHandleRelateDTO.getCidSheetNoListMap());
            formatRelateAmount(data, entityExt, sheetAttrPojo.getAttrPlan(), pidToAmountAdd, pidToAmountSub, sheetDeleteHandleRelateDTO.getPaymentIds(), amount, sheetDeleteHandleRelateDTO.getPidSheetIdListMap(), sheetDeleteHandleRelateDTO.getPidSheetNoListMap());
        }
    }

    /**
     * 删除回款单失败，则反向把金额回置，防止删除失败，其他业务更新。只回置合同/应收款的相关金额map
     * 比如，删除的回款单红冲总金额大于应收款的未收金额，如果删除后未收就为负数，因此该应收款下的回款单不能删除。此时，要更新的关联合同要增加or减少金额里面，要把这些不能删除的回款单的金额反向减少or增加
     * 注：金额正向影响参见setPaymentSheetDeleteHandleRelateDTO，反向金额取反即可
     * @param backContractFlag true回置合同金额，false回置应收款金额
     * @param sheetInfoPojo 回/付款单信息pojo
     * @param sheetDeleteHandleRelateDTO 删除/批量删除回款单，更新其关联业务（客户、合同、应收款）dto
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public void amountBack4DelError(boolean backContractFlag, SheetInfoPojo sheetInfoPojo, SheetDeleteHandleRelateDTO sheetDeleteHandleRelateDTO) throws XbbException {
        //预收款核销需要读取核销金额
        Double amount = sheetInfoPojo.getAmount();
        //删除失败，则反向影响，金额取反
        amount = Arith.mul(amount, -1);
        /*
         基于updateRelateList处理关联业务
         （1）对合同/应收款的已收、未收、坏账影响
         已核销、预收款核销：已收减少，未收增加
         红冲已核销、红冲预收款核销：已收增加，未收减少
         坏账：未收增加，坏账金额减少
         （2）对客户余额
         预收款：减少
         预收款核销、红冲预收款余额：增加
         */
        SheetTypeEnum sheetTypeEnum = sheetInfoPojo.getSheetTypeEnum();
        //默认：未收 + amount，已收 - amount
        Map<Long, Double> cidToAmountAdd = sheetDeleteHandleRelateDTO.getContractIdToUnAmount();
        Map<Long, Double> pidToAmountAdd = sheetDeleteHandleRelateDTO.getPaymentIdToUnAmount();
        Map<Long, Double> cidToAmountSub;
        Map<Long, Double> pidToAmountSub;
        switch (sheetTypeEnum) {
            case PREPAYMENT:
            case RED_PREPAYMENT:
            case PREPAY:
            case RED_PREPAY:
                /*
                预收款：客户余额 - amount
                红冲预收款：客户余额 + amount（红冲预收款余额的amount是负数，因此要取反一下，相当于客户余额 - amount）
                 */
                //预收款、红冲预收款不需要更新合同/应收款，直接返回
                return;
            case BAD_PAYMENT:
            case BAD_PAY:
                //坏账：未收 + amount，坏账 - amount
                cidToAmountSub = sheetDeleteHandleRelateDTO.getContractIdToBad();
                pidToAmountSub = sheetDeleteHandleRelateDTO.getPaymentIdToBad();
                break;
            default:
                cidToAmountSub = sheetDeleteHandleRelateDTO.getContractIdToAmount();
                pidToAmountSub = sheetDeleteHandleRelateDTO.getPaymentIdToAmount();
                break;
        }
        SheetAttrPojo sheetAttrPojo = sheetDeleteHandleRelateDTO.getSheetAttrPojo();
        if (backContractFlag) {
            formatRelateAmount4DelError(sheetInfoPojo.getData(), sheetAttrPojo.getAttrCon(), cidToAmountAdd, cidToAmountSub, amount);
        } else {
            formatRelateAmount4DelError(sheetInfoPojo.getData(), sheetAttrPojo.getAttrPlan(), pidToAmountAdd, pidToAmountSub, amount);
        }
    }

    /**
     * 封装关联业务id和dataId集合对应关系map
     * @param idToDataIdListMap 关联业务id-对应实体dataId集合
     * @param idToDataNoListMap 关联业务id-对应实体的编号集合
     * @param data 数据
     * @param entityExt 数据实体
     * @param attr 关联字段
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public void handleIdDataIdNoListMap(Map<Long, List<Long>> idToDataIdListMap, Map<Long, List<String>> idToDataNoListMap, JSONObject data, PaasFormDataEntityExt entityExt, String attr) throws XbbException {
        List<Long> idList = getJsonArrOrDefaultFromFormData(data, attr, new JSONArray()).toJavaList(Long.class);
        Long dataId = entityExt.getId();
        String serialNo = entityExt.getSerialNo();
        for (Long id : idList) {
            List<Long> dataIdList = idToDataIdListMap.getOrDefault(id, new ArrayList<>());
            List<String> noList = idToDataNoListMap.getOrDefault(id, new ArrayList<>());
            dataIdList.add(dataId);
            noList.add(serialNo);
            idToDataIdListMap.put(id, dataIdList);
            idToDataNoListMap.put(id, noList);
        }
    }

    /**
     * 设置要更新的字段的金额值
     * @param attr 要更新的字段
     * @param idToAmount 业务id-该业务要更新的金额
     * @param id 业务id
     * @param data 业务data
     * @param updateData 更新用data
     * @return java.lang.Double 新的金额
     * @author zcp
     * @date 19/12/6 006 20:17
     * @update 19/12/6 006 20:17
     * @since v1.0
     * @version v1.0
     */
    public Double handleAttrAmount(String attr, Map<Long, Double> idToAmount, Long id, JSONObject data, JSONObject updateData) {
        //业务要增加or减少的金额
        Double amount = idToAmount.getOrDefault(id, 0D);
        Double value = data.getDoubleValue(attr);
        if (amount != 0) {
            //原已收、未收金额小于0，则把其当做0去处理，以防错上加错
            if (value < BasicConstant.ZERO) {
                value = BasicConstant.ZERO_DOUBLE;
            }
            //新的金额不为0，才更新
            value = Arith.add(value, amount);
            updateData.put(attr, value);
        }
        return value;
    }

    /**
     * 处理不能删除的回款单，塞入错误提示信息，并从要删除的list内移除
     * @param noneDelMsg 不能删除的错误提示信息
     * @param errorTypeSet 不允许删除类型
     * @param errorDataSet 不允许删除数据
     * @param linkIdNoListMap 关联id-其对应回款单编号list
     * @param linkIdIdListMap 关联id-其对应回款单IDlist
     * @param delIdList 要删除的回款单id集合
     * @param delNoList 要删除的回款单编号集合
     * @param linkId 关联业务id
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public void removeInfo4DelError(String noneDelMsg, Set<String> errorTypeSet, Set<String> errorDataSet, Map<Long, List<String>> linkIdNoListMap, Map<Long, List<Long>> linkIdIdListMap, Collection<Long> delIdList, List<String> delNoList, Long linkId) {
        errorTypeSet.add(noneDelMsg);
        List<String> noneDelNos = linkIdNoListMap.getOrDefault(linkId, new ArrayList<>());
        //塞入不能删除的回款单编号
        errorDataSet.addAll(noneDelNos);
        //移除不能删除的回款单ids
        delIdList.removeAll(linkIdIdListMap.getOrDefault(linkId, new ArrayList<>()));
        //移除不能删除的回款单编号
        delNoList.removeAll(noneDelNos);
    }

    /**
     * 处理不能删除的回款单，塞入错误提示信息，并从要删除的list内移除
     * @param backContractFlag true回置合同金额，false回置应收款金额
     * @param noneDelMsg 不能删除的错误提示信息
     * @param errorTypeSet 不允许删除类型
     * @param errorDataSet 不允许删除数据
     * @param linkIdNoListMap 关联id-其对应回款单编号list
     * @param linkIdIdListMap 关联id-其对应回款单IDlist
     * @param delIdList 要删除的回款单id集合
     * @param delNoList 要删除的回款单编号集合
     * @param linkId 关联业务id
     * @param sheetDeleteHandleRelateDTO 删除/批量删除回款单，更新其关联业务（客户、合同、应收款）dto
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public void removeInfoAndAmountBack4DelError(boolean backContractFlag, String noneDelMsg, Set<String> errorTypeSet, Set<String> errorDataSet, Map<Long, List<String>> linkIdNoListMap, Map<Long, List<Long>> linkIdIdListMap, Collection<Long> delIdList, List<String> delNoList, Long linkId, SheetDeleteHandleRelateDTO sheetDeleteHandleRelateDTO) throws XbbException {
        errorTypeSet.add(noneDelMsg);
        List<String> noneDelNos = linkIdNoListMap.getOrDefault(linkId, new ArrayList<>());
        //塞入不能删除的回款单编号
        errorDataSet.addAll(noneDelNos);
        List<Long> noneDelSheetIds = linkIdIdListMap.getOrDefault(linkId, new ArrayList<>());
        Map<Long, SheetInfoPojo> sheetInfoMap = sheetDeleteHandleRelateDTO.getSheetInfoMap();
        Map<String, List<Long>> uuidSheetMap = sheetDeleteHandleRelateDTO.getUuidSheetMap();
        List<Long> otherTeamSheetIdList = new ArrayList<>();
        for (Long sheetId : noneDelSheetIds) {
            SheetInfoPojo sheetInfoPojo = sheetInfoMap.get(sheetId);
            if (sheetInfoPojo == null) {
                continue;
            }
            Integer alone = sheetInfoPojo.getAlone();
            String uuid = sheetInfoPojo.getUuid();
            if (Objects.equals(alone, SheetAloneEnum.CHILD.getCode()) && uuidSheetMap.containsKey(uuid)) {
                // 子单不能删，则其父单和父单下的其他所有子单都不可删除
                // 且其所对应的其他合同也不能删除（把金额反向置回），因为多个子单会关联不同的合同
                otherTeamSheetIdList.addAll(uuidSheetMap.get(uuid));
            }
            //把不能删除的数据重置回去
            amountBack4DelError(backContractFlag, sheetInfoPojo, sheetDeleteHandleRelateDTO);
        }
        //移除已经处理过的数据id
        otherTeamSheetIdList.removeAll(noneDelSheetIds);
        List<String> otherDelSheetNoList = new ArrayList<>();
        if (otherTeamSheetIdList.size() > 0) {
            for (Long sheetId : otherTeamSheetIdList) {
                SheetInfoPojo sheetInfoPojo = sheetInfoMap.get(sheetId);
                if (sheetInfoPojo == null) {
                    continue;
                }
                otherDelSheetNoList.add(sheetInfoPojo.getSerialNo());
                if (Objects.equals(sheetInfoPojo.getAlone(), SheetAloneEnum.CHILD.getCode())) {
                    //其他子单把不能删除的数据重置回去，父单不用回置
                    amountBack4DelError(backContractFlag, sheetInfoPojo, sheetDeleteHandleRelateDTO);
                }
            }
        }
        //移除不能删除的回款单ids
        delIdList.removeAll(noneDelSheetIds);
        delIdList.removeAll(otherTeamSheetIdList);
        //移除不能删除的回款单编号
        delNoList.removeAll(noneDelNos);
        delNoList.removeAll(otherDelSheetNoList);
    }

    /**
     * 处理不能删除的回款单，塞入错误提示信息，并从要删除的list内移除
     * @param noneDelMsg 不能删除的错误提示信息
     * @param noneDelIds 不能删除的错误idList
     * @param noneDelNos 不能删除的错误编号list
     * @param errorTypeSet 不允许删除类型
     * @param errorDataSet 不允许删除数据
     * @param delIdList 消息推送 id list
     * @param delNoList 消息推送 编号list
     * @param totalIdList 总的id lit
     */
    public void removeInvoiceRefNoneDel(String noneDelMsg,Set<Long> noneDelIds,Set<String> noneDelNos,Collection<String> errorTypeSet,Collection<String> errorDataSet,Collection<Long> delIdList, Collection<String> delNoList,List<Long> totalIdList){
        errorTypeSet.add(noneDelMsg);
        errorDataSet.addAll(noneDelNos);
        if(CollectionsUtil.isNotEmpty(totalIdList)){
            totalIdList.removeAll(noneDelIds);
        }
        if(CollectionsUtil.isNotEmpty(delIdList)){
            delIdList.removeAll(noneDelIds);
        }
        if(CollectionsUtil.isNotEmpty(delNoList)){
            delNoList.removeAll(noneDelNos);
        }
    }

    /**
     * 处理关联业务金额-增加or减少，并构建要删除的回款单及其关联合同/应收款的关联关系等
     * @param data 回款单data
     * @param entityExt 回款单实体
     * @param attr 关联字段attr，如关联合同、关联应收款
     * @param idToAmountAdd id和金额对应关系map：业务对应金额要增加
     * @param idToAmountSub id和金额对应关系map：业务对应金额要减少
     * @param ids 关联业务id集合
     * @param amount 金额
     * @param idSheetIdListMap 关联业务id-回款单id集合
     * @param idSheetNoListMap 关联业务id-回款单编号集合
     * @throws XbbException
     * @author zcp
     * @date 19/12/6 006 16:17
     * @update 19/12/6 006 16:17
     * @since v1.0
     * @version v1.0
     */
    private void formatRelateAmount(JSONObject data, PaasFormDataEntityExt entityExt, String attr, Map<Long, Double> idToAmountAdd, Map<Long, Double> idToAmountSub, Set<Long> ids, Double amount, Map<Long, List<Long>> idSheetIdListMap, Map<Long, List<String>> idSheetNoListMap) throws XbbException {
        if (Objects.equals(amount, BasicConstant.ZERO_DOUBLE)) {
            return;
        }
        List<Long> idList = getJsonArrOrDefaultFromFormData(data, attr, new JSONArray()).toJavaList(Long.class);
        if (idList.isEmpty()) {
            return;
        }
        Long dataId = entityExt.getId();
        String serialNo = entityExt.getSerialNo();
        for (Long id : idList) {
            ids.add(id);
            //加
            Double addValue = Arith.add(idToAmountAdd.getOrDefault(id, 0D), amount);
            idToAmountAdd.put(id, addValue);
            //减
            Double subValue = Arith.sub(idToAmountSub.getOrDefault(id, 0D), amount);
            idToAmountSub.put(id, subValue);
            List<Long> dataIdList = idSheetIdListMap.getOrDefault(id, new ArrayList<>());
            dataIdList.add(dataId);
            idSheetIdListMap.put(id, dataIdList);
            List<String> noList = idSheetNoListMap.getOrDefault(id, new ArrayList<>());
            noList.add(serialNo);
            idSheetNoListMap.put(id, noList);
        }
    }

    /**
     * 只处理关联业务金额-增加or减少（删除失败，反向去掉之前减少or增加的值，amount已经取反）
     * @param data 回款单data
     * @param attr 关联字段attr，如关联合同、关联应收款
     * @param idToAmountAdd id和金额对应关系map：业务对应金额要增加
     * @param idToAmountSub id和金额对应关系map：业务对应金额要减少
     * @param amount 金额
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void formatRelateAmount4DelError(JSONObject data, String attr, Map<Long, Double> idToAmountAdd, Map<Long, Double> idToAmountSub, Double amount) throws XbbException {
        if (Objects.equals(amount, BasicConstant.ZERO_DOUBLE)) {
            return;
        }
        List<Long> idList = getJsonArrOrDefaultFromFormData(data, attr, new JSONArray()).toJavaList(Long.class);
        if (idList.isEmpty()) {
            return;
        }
        for (Long id : idList) {
            //加
            Double addValue = Arith.add(idToAmountAdd.getOrDefault(id, 0D), amount);
            idToAmountAdd.put(id, addValue);
            //减
            Double subValue = Arith.sub(idToAmountSub.getOrDefault(id, 0D), amount);
            idToAmountSub.put(id, subValue);
        }
    }

    /**
     * 处理客户预收款余额-增加（正负外部处理好）
     * @param sheetAttrPojo 回款单/付款单的字段attr pojo
     * @param data 数据data
     * @param customerIdToBalance 客户id-该客户要增加/减少的金额
     * @param amount 金额
     * @author zcp
     * @date 19/12/6 006 16:28
     * @update 19/12/6 006 16:28
     * @since v1.0
     * @version v1.0
     */
    private void formatRelateCustomerBalance(SheetAttrPojo sheetAttrPojo, JSONObject data, Map<Long, Double> customerIdToBalance, Double amount) {
        if (Objects.equals(amount, BasicConstant.ZERO_DOUBLE)) {
            return;
        }
        Long customerId = data.getLongValue(sheetAttrPojo.getAttrCustomer());
        Double value = Arith.add(customerIdToBalance.getOrDefault(customerId, 0D), amount);
        customerIdToBalance.put(customerId, value);
    }

    /**
     * 合并地址数据
     * @param jsonObject 地址对象
     */
    public String mergeAddressData(JSONObject jsonObject) {
        if(jsonObject == null) {
            return null;
        }
        String province = jsonObject.getString(PaasConstant.PROVINCE) != null ? jsonObject.getString(PaasConstant.PROVINCE) : "";
        String city = jsonObject.getString(PaasConstant.CITY) != null ? jsonObject.getString(PaasConstant.CITY) : "";
        String district = jsonObject.getString(PaasConstant.DISTRICT) != null ? jsonObject.getString(PaasConstant.DISTRICT) : "";
        String address = jsonObject.getString(PaasConstant.ADDRESS) != null ? jsonObject.getString(PaasConstant.ADDRESS) : "";

        String fullAddress = province + city + district + address;
        return fullAddress;
    }

    /**
     * 设置默认值
     * 目前只支持日期的
     *
     * @param fieldAttrEntity 字段实体
     * @param data 数据
     * @author long.rao
     * @date 2020-01-07 17:01
     */
    public void setDefaultValue(FieldAttrEntity fieldAttrEntity, JSONObject data) {
        if (Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.DATETIME.getType())) {
            DefaultAttrPoJo defaultAttr = fieldAttrEntity.getDefaultAttr();
            if (Objects.nonNull(defaultAttr)) {
                String defaultType = defaultAttr.getDefaultType();
                Object defaultValue = defaultAttr.getDefaultValue();
                if (Objects.equals(defaultType, DefaultTypeEnum.TODAY.getAlias())) {
                    data.put(fieldAttrEntity.getAttr(), DateTimeUtil.getTodayInt());
                } else if (Objects.equals(defaultType, DefaultTypeEnum.CUSTOM.getAlias()) && Objects.nonNull(defaultValue) && !Objects.equals(defaultValue, 0)) {
                    data.put(fieldAttrEntity.getAttr(), defaultValue);
                }
            }
        }
    }

    public void mergeFailMsg(Collection failNames, Integer dataIdSize, TeamBatchPojo teamBatchPojo, String operateStr, TeamBatchVO teamBatchVO) {
        StringBuilder msg = new StringBuilder();
        if (dataIdSize == failNames.size()) {
            msg.append(I18nMessageUtil.getMessage(ListBatchConstant.ALL_DATA));
            teamBatchVO.setResultMsg(ListBatchResultEnum.OPERATE_FAIL.getMsg());
        } else {
            for (Object failName : failNames) {
                StringUtil.stringBuilderAppendSuffix(msg, "、", failName);
            }
            msg.append("，").append(failNames.size()).append(I18nMessageUtil.getMessage(ListBatchConstant.DATA));
            teamBatchVO.setResultMsg(ListBatchResultEnum.OPERATE_PART_FAIL.getMsg());
        }
        teamBatchVO.setResultType(ListBatchResultEnum.OPERATE_FAIL.getAlias());
        msg.append(operateStr).append(I18nMessageUtil.getMessage(ListBatchConstant.FAIL));
        teamBatchPojo.setFailMsg(msg.toString());
    }

    /**
     * 同样异常信息封装
     *
     * @param failNames
     * @param operateStr
     * @param errorCode
     * @param failMsgReson
     * @param teamBatchPojos
     */
    public void batchSameErrorPackage(Collection failNames, String operateStr, int errorCode, String failMsgReson, List<TeamBatchPojo> teamBatchPojos) {
        for (Object failName : failNames) {
            TeamBatchPojo teamBatchPojo = new TeamBatchPojo();
            teamBatchPojo.setCode(errorCode);
            teamBatchPojo.setFailMsgReason(failMsgReson);
            teamBatchPojo.setErrorName(failName);
            teamBatchPojo.setBasicOperate(operateStr);
            teamBatchPojos.add(teamBatchPojo);
        }
    }

    /**
     * 只需要拼接错误信息，不需要返回码，所以这边相对于mergeFailMsg少了几个传参
     *
     * @param failNames
     * @param dataIdSize
     * @param teamBatchPojo
     * @param operateStr
     */
    public void mergeFailMsgNoResultType(Collection failNames, Integer dataIdSize, TeamBatchPojo teamBatchPojo, String operateStr) {
        StringBuilder msg = new StringBuilder();
        if (dataIdSize == failNames.size()) {
            msg.append(I18nMessageUtil.getMessage(ListBatchConstant.ALL_DATA));
        } else {
            for (Object failName : failNames) {
                StringUtil.stringBuilderAppendSuffix(msg, "、", failName);
            }
            msg.append("，").append(failNames.size()).append(I18nMessageUtil.getMessage(ListBatchConstant.DATA));
        }
        msg.append(operateStr).append(I18nMessageUtil.getMessage(ListBatchConstant.FAIL));
        teamBatchPojo.setFailMsg(msg.toString());
    }

    /**
     * 单条数据异常信息封装
     *
     * @param errorName
     * @param operateStr
     * @param teamBatchPojo
     */
    public void singleFailMsg(String errorName, String operateStr, TeamBatchPojo teamBatchPojo) {
        /*StringBuilder msg = new StringBuilder();
        msg.append(errorName).append(",").append("1").append(I18nMessageUtil.getMessage(ListBatchConstant.DATA));
        msg.append(operateStr).append(I18nMessageUtil.getMessage(ListBatchConstant.FAIL));*/
        teamBatchPojo.setErrorName(errorName);
        teamBatchPojo.setBasicOperate(operateStr);
        //teamBatchPojo.setFailMsg(msg.toString());
    }

    /**
     * 对团队批量操作的异常信息的封装,合并同样的报错
     *
     * @param teamBatchPojos 异常信息实体
     * @param dataIdSize 操作数据数量
     * @param teamBatchVO 批量的返回实体
     */
    public void batchFailMsg(List<TeamBatchPojo> teamBatchPojos, Integer dataIdSize, TeamBatchVO teamBatchVO) {
        List<TeamBatchPojo> finalTeamBatchPojos = new ArrayList<>();
        TeamBatchPojo finalTeamBatchPojo = null;
        TeamBatchPojo firstTempBatchPojo = null;
        StringBuilder msg = null;
        if (teamBatchPojos.size() == dataIdSize) {
            teamBatchVO.setResultType(ListBatchResultEnum.OPERATE_FAIL.getAlias());
            teamBatchVO.setResultMsg(ListBatchResultEnum.OPERATE_FAIL.getMsg());
        }else if (teamBatchPojos.size() < dataIdSize) {
            teamBatchVO.setResultType(ListBatchResultEnum.OPERATE_PART_FAIL.getAlias());
            teamBatchVO.setResultMsg(ListBatchResultEnum.OPERATE_PART_FAIL.getMsg());
        }
        //开始合并一下异常信息
        Map<Integer,List<TeamBatchPojo>> teamBatchMap = teamBatchPojos.stream().collect(Collectors.groupingBy(TeamBatchPojo::getCode));
        int size = teamBatchMap.size();
        if (size == BasicConstant.ONE && teamBatchPojos.size() > BasicConstant.ONE) {
            //代表所有异常信息是一样的
            firstTempBatchPojo = teamBatchPojos.get(BasicConstant.ZERO);
            finalTeamBatchPojo = new TeamBatchPojo();
            msg = new StringBuilder();
            if(teamBatchPojos.size() == dataIdSize){
                //所有数据都报错
                msg.append(I18nMessageUtil.getMessage(ListBatchConstant.ALL_DATA)).append(firstTempBatchPojo.getBasicOperate()).append(I18nMessageUtil.getMessage(ListBatchConstant.FAIL));

            }else if(teamBatchPojos.size() < dataIdSize){
                //部分数据报错
                //拼接失败数据errorName
                for (TeamBatchPojo tempTeamBatchPojo : teamBatchPojos) {
                    StringUtil.stringBuilderAppendSuffix(msg, "、", tempTeamBatchPojo.getErrorName());
                }
                msg.append("，").append(teamBatchPojos.size()).append(I18nMessageUtil.getMessage(ListBatchConstant.DATA));
                msg.append(firstTempBatchPojo.getBasicOperate()).append(I18nMessageUtil.getMessage(ListBatchConstant.FAIL));
            }
            finalTeamBatchPojo.setFailMsg(msg.toString());
            finalTeamBatchPojo.setFailMsgReason(firstTempBatchPojo.getFailMsgReason());
            finalTeamBatchPojos.add(finalTeamBatchPojo);
            teamBatchVO.setTeamBatchPojos(finalTeamBatchPojos);
        }else {
            //代表有多个异常信息
            for (Map.Entry<Integer, List<TeamBatchPojo>> entry : teamBatchMap.entrySet()) {
                List<TeamBatchPojo> tempTeamBatchValueList = entry.getValue();
                finalTeamBatchPojo = new TeamBatchPojo();
                //因为同一种信息都已经分好组了，所以可以默认把第一个的基本操作、以及错误作为公用的
                msg = new StringBuilder();
                firstTempBatchPojo = tempTeamBatchValueList.get(BasicConstant.ZERO);
                for (TeamBatchPojo tempTeamBatchPojo : tempTeamBatchValueList) {
                    StringUtil.stringBuilderAppendSuffix(msg, "、", tempTeamBatchPojo.getErrorName());
                }
                msg.append("，").append(tempTeamBatchValueList.size()).append(I18nMessageUtil.getMessage(ListBatchConstant.DATA));
                msg.append(firstTempBatchPojo.getBasicOperate()).append(I18nMessageUtil.getMessage(ListBatchConstant.FAIL));
                finalTeamBatchPojo.setFailMsg(msg.toString());
                finalTeamBatchPojo.setFailMsgReason(firstTempBatchPojo.getFailMsgReason());
                finalTeamBatchPojos.add(finalTeamBatchPojo);
                teamBatchVO.setTeamBatchPojos(finalTeamBatchPojos);
            }
        }
    }


    /**
     * 判断corpid是否在黑名单内
     * @param corpid
     * @return boolean
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public boolean checkBlacklistBigDataCorp(String corpid) {
        //统计临时处理方法，暂时注释
        return false;
        /*
        //缓存前缀or companyConfig表内configAlias
        String filePrefix = CompanyConfigEnum.BLACKLIST_BIG_DATA_CORP.getAlias();
        String redisResult = paasRedisHelper.getValue(filePrefix, StringConstant.DEFAULT_CORPID);
        if (StringUtil.isEmpty(redisResult)) {
            CompanyConfigEntity configEntity = companyConfigModel.getByConfigAlias(filePrefix, StringConstant.DEFAULT_CORPID);
            if (Objects.nonNull(configEntity)) {
                redisResult = configEntity.getConfigValue();
                //置入缓存
                paasRedisHelper.setValue(filePrefix, StringConstant.DEFAULT_CORPID, redisResult, RedisTimeConstant.TEN_MINUTES);
            }
        }
        List<String> bigDataCorpList = JsonHelperUtil.parseArray(redisResult, String.class);
        return bigDataCorpList.contains(corpid);
        */
    }

    /**
     * 流程设置内字段需要显示的FieldType类型
     * 从李杰原PaasProcessTemplateServiceImpl#formFields方法内抽出，以便初始化经销商类型模板设置默认流程时也可以使用，保持与普通审批判断逻辑统一
     * @return java.util.List<java.lang.Integer>
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public List<Integer> processShowAttrFieldType(){
        return Arrays.asList(ShowTypeEnum.ALL.getCode(), ShowTypeEnum.EDIT.getCode(), ShowTypeEnum.EDIT_LIST.getCode(), ShowTypeEnum.EDIT_DETAIL.getCode());
    }
    /**
     * 流程设置内判断【系统字段】是否必填
     * 从李杰原PaasProcessTemplateServiceImpl#formFields方法内抽出，以便初始化经销商类型模板设置默认流程时也可以使用，保持与普通审批判断逻辑统一
     * @param editHide 是否在设计阶段展示
     * @param showType 字段showType类型
     * @param showTypeList 要在流程设计内显示的字段showType集合
     * @return boolean
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public boolean processNotShow(Integer editHide, Integer showType, List<Integer> showTypeList){
        //当前只只展示这几类：0:全部显示 1:新建编辑显示 4:新建编辑，列表显示 5:新建编辑，详情显示
        return Objects.equals(editHide, BasicConstant.ONE) || !showTypeList.contains(showType);
    }

    /**
     * @Author sunshaoxiang
     * @Description paas 流程设计界面 字段过滤方法
     * @param editHide 是否在设计阶段展示
     * @param showType 字段showType类型
     * @param showTypeList 要在流程设计内显示的字段showType集合
     * @return boolean
     **/
    public boolean paasProcessNotShow(Integer editHide, Integer showType, List<Integer> showTypeList){
        //当前只只展示这几类：0:全部显示 1:新建编辑显示 4:新建编辑，列表显示 5:新建编辑，详情显示
        return Objects.equals(editHide, BasicConstant.ONE) || (!showTypeList.contains(showType) && Objects.nonNull(showType));
    }

    /**
     * 关联新建时把下拉类型带入，处理其items，把选项的checked置为true
     * @param comboField 下拉类型字段实体
     * @param comboValue 原单下拉类型字段value
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public void handleLinkAdd4Combo(FieldAttrEntity comboField, String comboValue) {
        if (StringUtil.isEmpty(comboValue) || Objects.isNull(comboField)) {
            return;
        }
        List<ItemPoJo> comboItemList = comboField.getItems();
        if (comboItemList != null) {
            for (ItemPoJo itemPoJo : comboItemList) {
                if (Objects.equals(itemPoJo.getValue(), comboValue)) {
                    itemPoJo.setChecked(true);
                } else {
                    itemPoJo.setChecked(false);
                }
            }
        }
    }

    /**
     * 公司是否开启工作流
     *
     * @param corpid 公司id
     * @return true/fasle 是/否开启工作流
     */
    public boolean isOpenWorkFlow(String corpid) {
        String filePrefix = CompanyConfigEnum.WORKFLOW.getAlias();
        String redisResult = paasRedisHelper.getValue(filePrefix, corpid);
        if (StringUtil.isEmpty(redisResult)) {
            CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.WORKFLOW.getAlias(), corpid);
            if (Objects.nonNull(companyConfigEntity)) {
                redisResult = companyConfigEntity.getConfigValue();
                //置入缓存
                paasRedisHelper.setValue(filePrefix, corpid, redisResult, RedisTimeConstant.TEN_MINUTES);
            }
        }
        return Objects.equals("1", redisResult);
    }


    /**
     * 公司工作流节点最大数量限制
     *
     * @param corpid 公司id
     * @return 最大节点数量
     */
    public Integer maxWorkflowNode(String corpid) {
        String filePrefix = CompanyConfigEnum.MAX_WORKFLOW_NODE.getAlias();
        String redisResult = paasRedisHelper.getValue(filePrefix, corpid);
        if (StringUtil.isEmpty(redisResult)) {
            CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(filePrefix, corpid);
            if (Objects.nonNull(companyConfigEntity)) {
                redisResult = companyConfigEntity.getConfigValue();
                //置入缓存
                paasRedisHelper.setValue(filePrefix, corpid, redisResult, RedisTimeConstant.TEN_MINUTES);
            }else{
                return PaasConstant.MAX_WORKFLOW_NODE_COUNT;
            }
        }
        return Integer.parseInt(redisResult);
    }

    /**
     * 登录时候获取用公司是否客户公海池高级模式
     *
     * @param corpid 公司id
     * @return true/fasle 是/否公海池高级模式
     */
    public boolean isCustomerPublicPoolSeniorModelByLogin(String corpid,Integer feeType) throws XbbException{
        try {
            String filePrefix = CompanyConfigEnum.CUSTOMER_PUBLIC_POOL_SENIOR_MODEL.getAlias();
            String redisResult = paasRedisHelper.getValue(filePrefix, corpid);
            if (StringUtil.isEmpty(redisResult)) {
                CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.CUSTOMER_PUBLIC_POOL_SENIOR_MODEL.getAlias(), corpid);
                if (Objects.nonNull(companyConfigEntity)) {
                    redisResult = companyConfigEntity.getConfigValue();
                    //置入缓存
                    paasRedisHelper.setValue(filePrefix, corpid, redisResult, RedisTimeConstant.TEN_MINUTES);
                }
            }
            return Objects.equals("1", redisResult) && Objects.equals(PackageTypeEnum.ULTIMATE.getType(),feeType);
        } catch (Exception e) {
            LOG.error("获取否公海池高级模式corpid:{},异常信息：{}", corpid, e);
            return Boolean.FALSE;
        }
    }

    /**
     * 公司是否客户公海池高级模式
     *
     * @param corpid 公司id
     * @return true/fasle 是/否公海池高级模式
     */
    public boolean isCustomerPublicPoolSeniorModel(String corpid) throws XbbException{
        String filePrefix = CompanyConfigEnum.CUSTOMER_PUBLIC_POOL_SENIOR_MODEL.getAlias();
        String redisResult = paasRedisHelper.getValue(filePrefix, corpid);
        if (StringUtil.isEmpty(redisResult)) {
            CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.CUSTOMER_PUBLIC_POOL_SENIOR_MODEL.getAlias(), corpid);
            if (Objects.nonNull(companyConfigEntity)) {
                redisResult = companyConfigEntity.getConfigValue();
                //置入缓存
                paasRedisHelper.setValue(filePrefix, corpid, redisResult, RedisTimeConstant.TEN_MINUTES);
            }
        }
        return Objects.equals(packageHelp.getFeeType(corpid), PackageTypeEnum.ULTIMATE.getType()) && Objects.equals("1", redisResult);
    }
    /**
     * @description 当前公司是否开启跨应用菜单白名单
     * @author yanglei
     * @date 2022/12/1 16:50
     * @param corpid
     * @return boolean
     */
    public boolean isOpenMenuWhite(String corpid) throws XbbException{
        try{
            String filePrefix = RedisPrefixConstant.MENU_WHITE_LIST;
            String redisResult = paasRedisHelper.getValue(filePrefix, "0");
            if (StringUtil.isEmpty(redisResult)) {
                CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.MENU_WHITE.getAlias(), "0");
                if (Objects.nonNull(companyConfigEntity)) {
                    redisResult = companyConfigEntity.getConfigValue();
                    //置入缓存
                    paasRedisHelper.setValue(filePrefix, "0", redisResult, RedisTimeConstant.TEN_MINUTES);
                }
            }
            return StringUtil.isNotEmpty(redisResult) && JSON.parseArray(redisResult, String.class).contains(corpid);
        }catch (Exception e){
            LOG.error("获取菜单白名单列表报错",e);
            return false;
        }
    }

    /**
     * 开启客户公海池高级模式
     * @param corpid
     */
    public void openCustomerPublicPoolSeniorModel(String corpid) {
        companyConfigModel.save(corpid, CompanyConfigEnum.CUSTOMER_PUBLIC_POOL_SENIOR_MODEL.getAlias(), CompanyConfigEnum.CUSTOMER_PUBLIC_POOL_SENIOR_MODEL.getName(), CompanyConfigEnum.CUSTOMER_PUBLIC_POOL_SENIOR_MODEL.getValue());
        //置入缓存
        paasRedisHelper.setValue(CompanyConfigEnum.CUSTOMER_PUBLIC_POOL_SENIOR_MODEL.getAlias(), corpid, CompanyConfigEnum.CUSTOMER_PUBLIC_POOL_SENIOR_MODEL.getValue(), RedisTimeConstant.TEN_MINUTES);
    }

    /**
     * 删除正在切换客户公海池高级模式标识
     * @param corpid
     */
    public void deleteCustomerPublicPoolModelChangeSenior(String corpid) {
//        Set<String> syncCorpid = paasRedisHelper.getValue4Set(RedisPrefixConstant.CUSTOMER_PUBLIC_POOL_MODEL_CHANGE_SENIOR, RedisPrefixConstant.SYNCING);
//        if (Objects.nonNull(syncCorpid) && syncCorpid.contains(corpid)) {
//            syncCorpid.remove(corpid);
//            //置入缓存
//            paasRedisHelper.setValue4Set(RedisPrefixConstant.CUSTOMER_PUBLIC_POOL_MODEL_CHANGE_SENIOR, RedisPrefixConstant.SYNCING, syncCorpid, TimeConstant.SECONDS_PER_MONTH);
//        }
        CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.CUSTOMER_PUBLIC_POOL_CHANGE.getAlias(), corpid);
        if (Objects.nonNull(companyConfigEntity)) {
            companyConfigModel.deleteByKey(companyConfigEntity.getId(), corpid);
        }
    }

    /**
     * 删除正在切换线索公海池高级模式的redis标识
     * @param corpid
     */
    public void deleteCluePublicPoolModelChangeSenior(String corpid) {
//        Set<String> syncCorpid = paasRedisHelper.getValue4Set(RedisPrefixConstant.CLUE_PUBLIC_POOL_MODEL_CHANGE_SENIOR, RedisPrefixConstant.SYNCING);
//        if (Objects.nonNull(syncCorpid) && syncCorpid.contains(corpid)) {
//            syncCorpid.remove(corpid);
//            //置入缓存
//            paasRedisHelper.setValue4Set(RedisPrefixConstant.CLUE_PUBLIC_POOL_MODEL_CHANGE_SENIOR,  RedisPrefixConstant.SYNCING, syncCorpid, TimeConstant.SECONDS_PER_MONTH);
//        }

        CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.CLUE_PUBLIC_POOL_CHANGE.getAlias(), corpid);
        if (Objects.nonNull(companyConfigEntity)) {
            companyConfigModel.deleteByKey(companyConfigEntity.getId(), corpid);
        }
    }

    /**
     * 关闭客户公海池高级模式
     * @param corpid
     */
    public void closeCustomerPublicPoolSeniorModel(String corpid) {
        CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.CUSTOMER_PUBLIC_POOL_SENIOR_MODEL.getAlias(), corpid);
        if (Objects.nonNull(companyConfigEntity)) {
            companyConfigModel.deleteByKey(companyConfigEntity.getId(), corpid);
        }
        paasRedisHelper.removeValue(CompanyConfigEnum.CUSTOMER_PUBLIC_POOL_SENIOR_MODEL.getAlias(), corpid);
    }

    /**
     * 关闭客户公海池高级模式
     * @param corpid
     */
    public void closeCluePublicPoolSeniorModel(String corpid) {
        CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.CLUE_PUBLIC_POOL_SENIOR_MODEL.getAlias(), corpid);
        if (Objects.nonNull(companyConfigEntity)) {
            companyConfigModel.deleteByKey(companyConfigEntity.getId(), corpid);
        }
        paasRedisHelper.removeValue(CompanyConfigEnum.CLUE_PUBLIC_POOL_SENIOR_MODEL.getAlias(), corpid);
    }

    /**
     * 公司是否线索公海池高级模式
     *
     * @param corpid 公司id
     * @return true/fasle 是/否公海池高级模式
     */
    public boolean isCluePublicPoolSeniorModelByLogin(String corpid,Integer feeType) throws XbbException {
        try {
            String filePrefix = CompanyConfigEnum.CLUE_PUBLIC_POOL_SENIOR_MODEL.getAlias();
            String redisResult = paasRedisHelper.getValue(filePrefix, corpid);
            if (StringUtil.isEmpty(redisResult)) {
                CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.CLUE_PUBLIC_POOL_SENIOR_MODEL.getAlias(), corpid);
                if (Objects.nonNull(companyConfigEntity)) {
                    redisResult = companyConfigEntity.getConfigValue();
                    //置入缓存
                    paasRedisHelper.setValue(filePrefix, corpid, redisResult, RedisTimeConstant.TEN_MINUTES);
                }
            }
            return Objects.equals("1", redisResult) && Objects.equals(PackageTypeEnum.ULTIMATE.getType(),feeType);
        } catch (Exception exception) {
            LOG.error("公司是否线索公海池高级模式corpid:{},异常信息：{}", corpid, exception);
            return Boolean.FALSE;
        }
    }

    /**
     * 公司是否线索公海池高级模式
     *
     * @param corpid 公司id
     * @return true/fasle 是/否公海池高级模式
     */
    public boolean isCluePublicPoolSeniorModel(String corpid) throws XbbException {
        String filePrefix = CompanyConfigEnum.CLUE_PUBLIC_POOL_SENIOR_MODEL.getAlias();
        String redisResult = paasRedisHelper.getValue(filePrefix, corpid);
        if (StringUtil.isEmpty(redisResult)) {
            CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.CLUE_PUBLIC_POOL_SENIOR_MODEL.getAlias(), corpid);
            if (Objects.nonNull(companyConfigEntity)) {
                redisResult = companyConfigEntity.getConfigValue();
                //置入缓存
                paasRedisHelper.setValue(filePrefix, corpid, redisResult, RedisTimeConstant.TEN_MINUTES);
            }
        }
        return Objects.equals(packageHelp.getFeeType(corpid), PackageTypeEnum.ULTIMATE.getType()) && Objects.equals("1", redisResult);
    }


    /**
     * 公海池高级模式
     * @param corpid
     */
    public void openCluePublicPoolSeniorModel(String corpid) {
        companyConfigModel.save(corpid, CompanyConfigEnum.CLUE_PUBLIC_POOL_SENIOR_MODEL.getAlias(), CompanyConfigEnum.CLUE_PUBLIC_POOL_SENIOR_MODEL.getName(), CompanyConfigEnum.CLUE_PUBLIC_POOL_SENIOR_MODEL.getValue());
        //置入缓存
        paasRedisHelper.setValue(CompanyConfigEnum.CLUE_PUBLIC_POOL_SENIOR_MODEL.getAlias(), corpid, CompanyConfigEnum.CLUE_PUBLIC_POOL_SENIOR_MODEL.getValue(), RedisTimeConstant.TEN_MINUTES);
    }

    /**
     * 根据业务判断线索和客户是否属于高级模式
     * @param corpid
     */
    public boolean isPublicPoolSeniorModel(String corpid, Integer businessType) throws XbbException{
        boolean isPublicPoolSeniorModel = Boolean.FALSE;
        if (Objects.equals(businessType,XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())) {
            isPublicPoolSeniorModel = isCustomerPublicPoolSeniorModel(corpid);
        } else if (Objects.equals(businessType,XbbRefTypeEnum.CLUE.getCode())){
            isPublicPoolSeniorModel = isCluePublicPoolSeniorModel(corpid);
        }
        return isPublicPoolSeniorModel;
     }

    /**
     * 开启工作流
     * @param corpid
     */
    public void openWorkflow(String corpid) {
        companyConfigModel.save(corpid, CompanyConfigEnum.WORKFLOW.getAlias(), CompanyConfigEnum.WORKFLOW.getName(), CompanyConfigEnum.WORKFLOW.getValue());
        //置入缓存
        paasRedisHelper.setValue(CompanyConfigEnum.WORKFLOW.getAlias(), corpid, CompanyConfigEnum.WORKFLOW.getValue(), RedisTimeConstant.TEN_MINUTES);
    }


    /**
     * 根据查询条件判断判重（bom版本判重，子产品条码查重等）
     *
     * @param formDataRepeatDTO 查重条件dto
     * @param conditionList 查重条件
     * @author chaoxiong.lei
     * @date 17:15 2019/2/19
     * @since v1.0
     **/
    public FormDataRepeatVO isUniqueByConditions(FormDataRepeatDTO formDataRepeatDTO, List<ConditionsEntityExt> conditionList) throws XbbException {
        FormDataRepeatVO formDataRepeatVO = new FormDataRepeatVO();
        try {
            if(formDataRepeatDTO.getValue() == null){
                return formDataRepeatVO;
            }
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            BoolQueryBuilder boolQueryBuilder = boolQuery();
            // 解析查询条件
            Integer businessType = formDataRepeatDTO.getBusinessType();
            IndexTypeEnum indexTypeEnum = indexTypeModel.getIndexType(formDataRepeatDTO.getCorpid(), businessType, formDataRepeatDTO.getSaasMark());
            EsUtil.parseCondition(boolQueryBuilder, conditionList, null,null,null);
            // 拼接appId，菜单id等数据
            EsUtil.getTermQuery(boolQueryBuilder, formDataRepeatDTO, true);
            String attr = "data." + formDataRepeatDTO.getAttr();
            attr = attr + ".keyword";
            boolQueryBuilder.filter(termQuery(attr, formDataRepeatDTO.getValue()));
            boolQueryBuilder.filter(termQuery(ParameterConstant.DEL, DelEnum.NORMAL.getDel()));
            // 业务类型

            if (indexTypeEnum == IndexTypeEnum.UNKNOWN) {
                throw new XbbException(TemplateErrorCodeEnum.API_ERROR_350006);
            }
            boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD,indexTypeEnum.getType()));
            SearchRequest searchRequest = new SearchRequest(indexTypeEnum.getIndex());
            // 没找到page相关的代码，默认查询20条 2019年6月27日11:08:03
            PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 1, 10);
            sourceBuilder.query(boolQueryBuilder);
            searchRequest.source(sourceBuilder);
            formDataRepeatVO = paasFormDataEsModel.repeat(pageRequest,searchRequest);
        }catch (Exception e) {
            LOG.error("commonHelp.repeat查询数据重复出错", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return formDataRepeatVO;
    }

    /**
     * 场景：国际化开启，英文环境下，图表中心修改分类名或者图表名称，前端传的nameEn的值会赋值给name字段
     *      因此在这里做一个特殊校验，根据环境分别判断name和nameEn的长度，返回对应的错误信息
     *      这里根据field判断是否修改了name，如果其他字段报错，则提示对应的错误信息
     * @param fieldError 图表中心分类和图表pojo的错误
     * @author yanglei
     */
    public void checkNameLength(FieldError fieldError) throws XbbException {
        String field = fieldError.getField();
        if (Objects.equals(LocaleContextHolder.getLocale().getLanguage(), I18nStringConstant.ENGLISH_LANGUAGE) && field.contains("name")){
            String rejectedValue = Objects.toString(fieldError.getRejectedValue());
            if (rejectedValue.length() > ChartConstant.NAME_EN_MAX || rejectedValue.length() < BasicConstant.ONE){
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, StatisticsServicesErrorCodeEnum.API_ERROR_224074.getMsg(),ChartConstant.NAME_EN_MAX);
            }
        }else {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, fieldError.getDefaultMessage());
        }
    };

    /**
     * 图表实体交换name和nameEn的值，
     * @param chart
     * @throws XbbException
     */
    public void exchangeChartNameAndNameEn(ChartEntity chart) {
        String name = chart.getName();
        String nameEn = chart.getNameEn();
        chart.setName(name);
        chart.setNameEn(nameEn);
    };

    /**
     * 图表分类体交换name和nameEn的值，
     * @param chartCategory
     * @throws XbbException
     */
    public void exchangeChartNameAndNameEn(ChartCategoryEntity chartCategory) {
        String name = chartCategory.getName();
        String nameEn = chartCategory.getNameEn();
        chartCategory.setName(name);
        chartCategory.setNameEn(nameEn);
    };

    /**
     * 公司是否展示业绩指标离职人员
     *
     * @param corpid 公司id
     * @return true/fasle 是/否展示
     */
    public boolean isShowPerformLeave(String corpid) {
        // 从缓存取
        String alias = CompanyConfigEnum.PERFORMANCE_LEAVE.getAlias();
        String filePrefix = RedisConstant.KEY_COMPANY_CONFIG;
        String key = corpid + StringConstant.COLON + alias;
        String value = paasRedisHelper.getValue(filePrefix, key);
        if (StringUtil.isEmpty(value)) {
            CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.PERFORMANCE_LEAVE.getAlias(), corpid);
            if (Objects.nonNull(companyConfigEntity)) {
                value = companyConfigEntity.getConfigValue();
            } else {
                // 兼容老数据
                value = CompanyConfigEnum.PERFORMANCE_LEAVE.getValue();
            }
            //置入缓存
            paasRedisHelper.setValue(filePrefix, key, value, TimeConstant.SECONDS_EIGHT_HOUR);
        }
        return Objects.equals("1", value);
    }

    /**
     * 公司是否展示固定数值业绩指标离职人员
     *
     * @param corpid 公司id
     * @return true/fasle 是/否展示
     */
    public boolean isShowPerformFixedLeave(String corpid) {
        // 从缓存取
        String alias = CompanyConfigEnum.FIXED_LEAVE_MARK.getAlias();
        String filePrefix = RedisConstant.KEY_COMPANY_CONFIG;
        String key = corpid + StringConstant.COLON + alias;
        String value = paasRedisHelper.getValue(filePrefix, key);
        if (StringUtil.isEmpty(value)) {
            CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.FIXED_LEAVE_MARK.getAlias(), corpid);
            if (Objects.nonNull(companyConfigEntity)) {
                value = companyConfigEntity.getConfigValue();
            } else {
                // 兼容老数据
                value = CompanyConfigEnum.FIXED_LEAVE_MARK.getValue();
            }
            //置入缓存
            paasRedisHelper.setValue(filePrefix, key, value, TimeConstant.SECONDS_EIGHT_HOUR);
        }
        return Objects.equals("1", value);
    }

    /**
     * 获取图表限制
     * @param corpid
     * @param alias
     * @return
     */
    public Integer getChartLimit(String corpid,String alias) throws XbbException {
        // 从缓存取
        String filePrefix = RedisConstant.KEY_COMPANY_CONFIG;
        String key = corpid + StringConstant.COLON + alias;
        String value = paasRedisHelper.getValue(filePrefix, key);
        if (StringUtil.isEmpty(value)) {
            ChartLimitTypeEnum typeEnum = ChartLimitTypeEnum.getByAlias(alias);
            if (null == typeEnum){
                LOG.error("获取配置失败");
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002);
            }
            ChartLimitEntity chartLimitEntity = chartLimitModel.getByAlias(alias,corpid);
            if (null != chartLimitEntity){
                value = String.valueOf(chartLimitEntity.getLimitNum());
            }else {
                switch (typeEnum){
                    case CHART_NUM_LIMIT:
                        value = String.valueOf(SizeConstant.CHART_LIMIT_NUM);
                        break;
                    case BI_DATA_NUM_LIMIT:
                        value = String.valueOf(SizeConstant.BI_DATA_LIMIT);
                        break;
                    case BI_MUITI_DATA_NUM_LIMIT:
                        value = String.valueOf(SizeConstant.BI_MUITI_DATA_LIMIT);
                        break;
                }
            }

            //置入缓存
            paasRedisHelper.setValue(filePrefix, key, value, TimeConstant.SECONDS_PER_HOUR);
        }
        return Integer.parseInt(value);
    }


    /**
     * 公司是否有经典模式
     *
     * @param corpid 公司id
     * @return true/fasle 是/否展示
     */
    public boolean hasClassicMode(String corpid) {
        // 从缓存取
        String alias = CompanyConfigEnum.MOBILE_INDEX_MODE.getAlias();
        String filePrefix = RedisConstant.KEY_COMPANY_CONFIG;
        String key = corpid + StringConstant.COLON + alias;
        String value = paasRedisHelper.getValue(filePrefix, key);
        if (StringUtil.isEmpty(value)) {
            CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.MOBILE_INDEX_MODE.getAlias(), corpid);
            if (Objects.nonNull(companyConfigEntity)) {
                value = companyConfigEntity.getConfigValue();
                try{
                    JSONArray jsonArray = JSONArray.parseArray(value);
                    List<Integer> list = jsonArray.toJavaList(Integer.class);
                    for (Integer item : list) {
                        if (Objects.equals(item, MobileIndexModeEnum.CLASSIC_MODE.getCode())) {
                            return true;
                        }
                    }
                }catch (Exception e){
                    LOG.error("缓存配置转list错误");
                }
            } else {
                // 兼容老数据
                value = CompanyConfigEnum.MOBILE_INDEX_MODE.getValue();
            }
            //置入缓存
            paasRedisHelper.setValue(filePrefix, key, value, TimeConstant.SECONDS_EIGHT_HOUR);
        }
        return false;
    }

    /**
     * 不需要过滤的首页看板
     * @param aliasEnum
     * @return
     */
    public boolean notFilterBoard(ChartCategoryAliasEnum aliasEnum) {
        // 目标管理，公告，销售简报不过虑
        if (aliasEnum == ChartCategoryAliasEnum.ANNOUNCEMENT
                || aliasEnum == ChartCategoryAliasEnum.SALES_REPORT
                || aliasEnum == ChartCategoryAliasEnum.PERFORMANCE) {
            return true;
        }
        // 过虑无对应saas业务的模块
        return aliasEnum.getProPermissionAlias().equals(XbbRefTypeEnum.UNKNOWN.getAlias());
    }

    /**
     * 获取特定类型阶段条件封装
     * @param corpid 公司id
     * @param stageFindPojo
     * @return java.util.Map<java.lang.String,java.lang.Object>
     * @throws
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public Map<String, Object> packageFilter(String corpid, StageFindPojo stageFindPojo){
        EnableEnum enableEnum = stageFindPojo.getEnableEnum();
        Long formId = stageFindPojo.getFormId();
        Long stageProcessId = stageFindPojo.getStageProcessId();
        List<Integer> typeIn = stageFindPojo.getTypeIn();
        List<String> codeIn = stageFindPojo.getCodeIn();
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put(StringConstant.CORPID, corpid);
        param.put(ParameterConstant.DEL, 0);
        if (Objects.nonNull(formId) && formId > 0) {
            param.put(StringConstant.FORM_ID,formId);
        }
        if (Objects.nonNull(stageProcessId) && stageProcessId > 0) {
            param.put(ParameterConstant.STAGE_PROCESS_ID,stageProcessId);
        }
        if (Objects.nonNull(enableEnum)) {
            param.put(ParameterConstant.ENABLE, enableEnum.getCode());
        }
        if (Objects.nonNull(typeIn)) {
            param.put("typeIn", typeIn);
        }
        if (CollectionUtils.isNotEmpty(codeIn)) {
            param.put("codeIn", codeIn);
        }
        return param;
    }

    /**
     * 排序阶段，普通阶段在结束阶段之前业务端已限制，然后把特殊阶段再做一次排序（因为结束阶段的三种类型可以随意更改顺序，而漏斗需要特殊阶段也有固定顺序-同样类型的特殊类型阶段之间不用再特殊排序处理）：强制按照“成功 - 失败 - 取消”排序
     * @param stageList 待排序阶段
     * @return java.util.List<? extends com.xbongbong.paas.domain.entity.PaasStageEntity>
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public List<? extends PaasStageEntity> sortStageList(List<? extends PaasStageEntity> stageList) {
        //类型 - 该类型下的阶段集合
        Map<Integer, List> notOrdinaryTypeMap = StageTypeEnum.getEndTypeMap();
        Iterator<? extends PaasStageEntity> iterator = stageList.iterator();
        while (iterator.hasNext()) {
            PaasStageEntity paasStageEntity = iterator.next();
            List list = notOrdinaryTypeMap.get(paasStageEntity.getType());
            //结束阶段已初始化进map
            if (Objects.nonNull(list)) {
                list.add(paasStageEntity);
                iterator.remove();
            }
        }
        for (Map.Entry<Integer, List> entry : notOrdinaryTypeMap.entrySet()) {
            stageList.addAll(entry.getValue());
        }
        return stageList;
    }
    /**
     * 获取当前公司套餐信息
     *
     * @param corpid 公司id
     * @return
     */
    public Integer getCurrentCompanyPackage(String corpid) throws XbbException {
        BasePackageInfoVO basePackageInfoVO = packageHelp.getBasePackageInfoFromRedis(corpid);
        Integer feeType = basePackageInfoVO.getFeeType();
        return feeType;
    }

    /**
     *
     * 获取当前产品的数据隔离权限
     * @param
     * @return true为隔离模式 false共享模式
     */
    public boolean getProductDataIsolation(String corpid,Long formId) throws XbbException {
        boolean isoLation = false;
        FormConfigEntity config = formConfigModel.getByConfig(FormConfigEnum.PRODUCT_DATA_ISOLATION.getConfig(), corpid, formId, SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.PRODUCT.getCode());
        // 如果数据库没有 就默认的共享模式,有且为1时为共享
        if (Objects.nonNull(config)){
            String configValue = config.getConfigValue();
            if (Objects.equals(configValue,"1")){
                // 配置值为1时数据隔离模式
                isoLation = true;
            }
        }
        return isoLation;

    }


    /**
     * 公司是否开启wps在线编辑
     *
     * @param corpid 公司id
     * @return true/fasle 是/否开启wps在线编辑
     */
    public boolean isOpenOnlineEditor(String corpid) {
        String filePrefix = CompanyConfigEnum.ONLINE_EDITOR_WHITE.getAlias();
        String redisResult = paasRedisHelper.getValue(filePrefix, corpid);
        if (StringUtil.isEmpty(redisResult)) {
            CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.ONLINE_EDITOR_WHITE.getAlias(), corpid);
            if (Objects.nonNull(companyConfigEntity)) {
                redisResult = companyConfigEntity.getConfigValue();
                //置入缓存
                paasRedisHelper.setValue(filePrefix, corpid, redisResult, RedisTimeConstant.TEN_MINUTES);
            }
        }
        return Objects.equals("1", redisResult);
    }
}
